Skip to content

Commit 1c5be70

Browse files
authored
Merge pull request swiftlang#33995 from DougGregor/conformance-checker-missing-witnesses
[Conformance checker] Capture potential matches for missing witnesses.
2 parents 493d567 + f956e4f commit 1c5be70

File tree

6 files changed

+125
-51
lines changed

6 files changed

+125
-51
lines changed

include/swift/AST/ASTContext.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ class ConstraintCheckerArenaRAII {
199199

200200
class SILLayout; // From SIL
201201

202+
/// A set of missing witnesses for a given conformance. These are temporarily
203+
/// stashed in the ASTContext so the type checker can get at them.
204+
///
205+
/// The only subclass is owned by the type checker, so it can hide its own
206+
/// data structures.
207+
class MissingWitnessesBase {
208+
public:
209+
virtual ~MissingWitnessesBase();
210+
};
211+
202212
/// ASTContext - This object creates and owns the AST objects.
203213
/// However, this class does more than just maintain context within an AST.
204214
/// It is the closest thing to thread-local or compile-local storage in this
@@ -949,12 +959,13 @@ class ASTContext final {
949959
takeDelayedConformanceDiags(NormalProtocolConformance *conformance);
950960

951961
/// Add delayed missing witnesses for the given normal protocol conformance.
952-
void addDelayedMissingWitnesses(NormalProtocolConformance *conformance,
953-
ArrayRef<ValueDecl*> witnesses);
962+
void addDelayedMissingWitnesses(
963+
NormalProtocolConformance *conformance,
964+
std::unique_ptr<MissingWitnessesBase> missingWitnesses);
954965

955966
/// Retrieve the delayed missing witnesses for the given normal protocol
956967
/// conformance.
957-
std::vector<ValueDecl*>
968+
std::unique_ptr<MissingWitnessesBase>
958969
takeDelayedMissingWitnesses(NormalProtocolConformance *conformance);
959970

960971
/// Produce a specialized conformance, which takes a generic

lib/AST/ASTContext.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,8 @@ struct ASTContext::Implementation {
298298
/// Map from normal protocol conformances to missing witnesses that have
299299
/// been delayed until the conformance is fully checked, so that we can
300300
/// issue a fixit that fills the entire protocol stub.
301-
llvm::DenseMap<NormalProtocolConformance *, std::vector<ValueDecl*>>
301+
llvm::DenseMap<
302+
NormalProtocolConformance *, std::unique_ptr<MissingWitnessesBase>>
302303
DelayedMissingWitnesses;
303304

304305
/// Stores information about lazy deserialization of various declarations.
@@ -2139,22 +2140,24 @@ bool ASTContext::hasDelayedConformanceErrors() const {
21392140
return false;
21402141
}
21412142

2143+
MissingWitnessesBase::~MissingWitnessesBase() { }
2144+
21422145
void ASTContext::addDelayedConformanceDiag(
21432146
NormalProtocolConformance *conformance,
21442147
DelayedConformanceDiag fn) {
21452148
getImpl().DelayedConformanceDiags[conformance].push_back(std::move(fn));
21462149
}
21472150

2148-
void ASTContext::
2149-
addDelayedMissingWitnesses(NormalProtocolConformance *conformance,
2150-
ArrayRef<ValueDecl*> witnesses) {
2151-
auto &bucket = getImpl().DelayedMissingWitnesses[conformance];
2152-
bucket.insert(bucket.end(), witnesses.begin(), witnesses.end());
2151+
void ASTContext::addDelayedMissingWitnesses(
2152+
NormalProtocolConformance *conformance,
2153+
std::unique_ptr<MissingWitnessesBase> missingWitnesses) {
2154+
getImpl().DelayedMissingWitnesses[conformance] = std::move(missingWitnesses);
21532155
}
21542156

2155-
std::vector<ValueDecl*> ASTContext::
2156-
takeDelayedMissingWitnesses(NormalProtocolConformance *conformance) {
2157-
std::vector<ValueDecl*> result;
2157+
std::unique_ptr<MissingWitnessesBase>
2158+
ASTContext::takeDelayedMissingWitnesses(
2159+
NormalProtocolConformance *conformance) {
2160+
std::unique_ptr<MissingWitnessesBase> result;
21582161
auto known = getImpl().DelayedMissingWitnesses.find(conformance);
21592162
if (known != getImpl().DelayedMissingWitnesses.end()) {
21602163
result = std::move(known->second);

lib/Sema/CSDiagnostics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2865,7 +2865,7 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
28652865
{
28662866
llvm::SmallString<128> Text;
28672867
llvm::raw_svector_ostream SS(Text);
2868-
llvm::SetVector<ValueDecl *> missingWitnesses;
2868+
llvm::SetVector<MissingWitness> missingWitnesses;
28692869
for (auto protocol : missingProtocols) {
28702870
auto conformance = NormalProtocolConformance(
28712871
nominal->getDeclaredType(), protocol, SourceLoc(), nominal,
@@ -2877,7 +2877,7 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
28772877
}
28782878

28792879
for (auto decl : missingWitnesses) {
2880-
swift::printRequirementStub(decl, nominal, nominal->getDeclaredType(),
2880+
swift::printRequirementStub(decl.requirement, nominal, nominal->getDeclaredType(),
28812881
nominal->getStartLoc(), SS);
28822882
}
28832883

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,7 @@ class swift::MultiConformanceChecker {
15031503
llvm::SmallVector<ValueDecl*, 16> UnsatisfiedReqs;
15041504
llvm::SmallVector<ConformanceChecker, 4> AllUsedCheckers;
15051505
llvm::SmallVector<NormalProtocolConformance*, 4> AllConformances;
1506-
llvm::SetVector<ValueDecl*> MissingWitnesses;
1506+
llvm::SetVector<MissingWitness> MissingWitnesses;
15071507
llvm::SmallPtrSet<ValueDecl *, 8> CoveredMembers;
15081508

15091509
/// Check one conformance.
@@ -1733,13 +1733,17 @@ checkIndividualConformance(NormalProtocolConformance *conformance,
17331733
PrettyStackTraceConformance trace(getASTContext(), "type-checking",
17341734
conformance);
17351735

1736-
std::vector<ValueDecl*> revivedMissingWitnesses;
1736+
std::vector<MissingWitness> revivedMissingWitnesses;
17371737
switch (conformance->getState()) {
17381738
case ProtocolConformanceState::Incomplete:
17391739
if (conformance->isInvalid()) {
17401740
// Revive registered missing witnesses to handle it below.
1741-
revivedMissingWitnesses =
1742-
getASTContext().takeDelayedMissingWitnesses(conformance);
1741+
if (auto delayed = getASTContext().takeDelayedMissingWitnesses(
1742+
conformance)) {
1743+
revivedMissingWitnesses = std::move(
1744+
static_cast<DelayedMissingWitnesses *>(
1745+
delayed.get())->missingWitnesses);
1746+
}
17431747

17441748
// If we have no missing witnesses for this invalid conformance, the
17451749
// conformance is invalid for other reasons, so emit diagnosis now.
@@ -2503,7 +2507,7 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
25032507

25042508
ConformanceChecker::ConformanceChecker(
25052509
ASTContext &ctx, NormalProtocolConformance *conformance,
2506-
llvm::SetVector<ValueDecl *> &GlobalMissingWitnesses,
2510+
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
25072511
bool suppressDiagnostics)
25082512
: WitnessChecker(ctx, conformance->getProtocol(), conformance->getType(),
25092513
conformance->getDeclContext()),
@@ -3008,26 +3012,28 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
30083012
/// NoStubRequirements.
30093013
static void
30103014
printProtocolStubFixitString(SourceLoc TypeLoc, ProtocolConformance *Conf,
3011-
ArrayRef<ValueDecl*> MissingWitnesses,
3015+
ArrayRef<MissingWitness> MissingWitnesses,
30123016
std::string &FixitString,
30133017
llvm::SetVector<ValueDecl*> &NoStubRequirements) {
30143018
llvm::raw_string_ostream FixitStream(FixitString);
30153019
std::for_each(MissingWitnesses.begin(), MissingWitnesses.end(),
3016-
[&](ValueDecl* VD) {
3017-
if (!printRequirementStub(VD, Conf->getDeclContext(), Conf->getType(),
3018-
TypeLoc, FixitStream)) {
3019-
NoStubRequirements.insert(VD);
3020+
[&](const MissingWitness &Missing) {
3021+
if (!printRequirementStub(
3022+
Missing.requirement, Conf->getDeclContext(), Conf->getType(),
3023+
TypeLoc, FixitStream)) {
3024+
NoStubRequirements.insert(Missing.requirement);
30203025
}
30213026
});
30223027
}
30233028

30243029
/// Filter the given array of protocol requirements and produce a new vector
30253030
/// containing the non-conflicting requirements to be implemented by the given
30263031
/// \c Adoptee type.
3027-
static llvm::SmallVector<ValueDecl *, 4>
3028-
filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
3029-
llvm::SmallVector<ValueDecl *, 4> Filtered;
3030-
if (Reqs.empty()) {
3032+
static llvm::SmallVector<MissingWitness, 4>
3033+
filterProtocolRequirements(
3034+
ArrayRef<MissingWitness> MissingWitnesses, Type Adoptee) {
3035+
llvm::SmallVector<MissingWitness, 4> Filtered;
3036+
if (MissingWitnesses.empty()) {
30313037
return Filtered;
30323038
}
30333039

@@ -3039,10 +3045,11 @@ filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
30393045

30403046
llvm::SmallDenseMap<DeclName, llvm::SmallVector<ValueDecl *, 2>, 4>
30413047
DeclsByName;
3042-
for (auto *const Req : Reqs) {
3048+
for (const auto &Missing: MissingWitnesses) {
3049+
auto Req = Missing.requirement;
30433050
if (DeclsByName.find(Req->getName()) == DeclsByName.end()) {
30443051
DeclsByName[Req->getName()] = {Req};
3045-
Filtered.push_back(Req);
3052+
Filtered.push_back(Missing);
30463053
continue;
30473054
}
30483055

@@ -3068,7 +3075,7 @@ filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
30683075
}
30693076

30703077
DeclsByName[Req->getName()].push_back(Req);
3071-
Filtered.push_back(Req);
3078+
Filtered.push_back(Missing);
30723079
}
30733080

30743081
return Filtered;
@@ -3082,10 +3089,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
30823089
if (LocalMissing.empty())
30833090
return;
30843091

3085-
const auto InsertFixit = [](NormalProtocolConformance *Conf,
3086-
SourceLoc ComplainLoc, bool EditorMode,
3087-
llvm::SmallVector<ValueDecl *, 4>
3088-
MissingWitnesses) {
3092+
const auto InsertFixit = [](
3093+
NormalProtocolConformance *Conf, SourceLoc ComplainLoc, bool EditorMode,
3094+
llvm::SmallVector<MissingWitness, 4> MissingWitnesses) {
30893095
DeclContext *DC = Conf->getDeclContext();
30903096
// The location where to insert stubs.
30913097
SourceLoc FixitLocation;
@@ -3119,7 +3125,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
31193125
}
31203126
auto &SM = DC->getASTContext().SourceMgr;
31213127
auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation);
3122-
for (auto VD : MissingWitnesses) {
3128+
for (const auto &Missing : MissingWitnesses) {
3129+
auto VD = Missing.requirement;
3130+
31233131
// Don't ever emit a diagnostic for a requirement in the NSObject
31243132
// protocol. They're not implementable.
31253133
if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl()))
@@ -3189,18 +3197,22 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
31893197
// If the diagnostics are suppressed, we register these missing witnesses
31903198
// for later revisiting.
31913199
Conformance->setInvalid();
3192-
getASTContext().addDelayedMissingWitnesses(Conformance, MissingWitnesses);
3200+
getASTContext().addDelayedMissingWitnesses(
3201+
Conformance,
3202+
std::make_unique<DelayedMissingWitnesses>(MissingWitnesses));
31933203
} else {
31943204
diagnoseOrDefer(
3195-
LocalMissing[0], true, [&](NormalProtocolConformance *Conf) {
3205+
LocalMissing[0].requirement, true,
3206+
[&](NormalProtocolConformance *Conf) {
31963207
InsertFixit(Conf, Loc, IsEditorMode, std::move(MissingWitnesses));
31973208
});
31983209
}
31993210
clearGlobalMissingWitnesses();
32003211
return;
32013212
}
32023213
case MissingWitnessDiagnosisKind::ErrorOnly: {
3203-
diagnoseOrDefer(LocalMissing[0], true, [](NormalProtocolConformance *) {});
3214+
diagnoseOrDefer(
3215+
LocalMissing[0].requirement, true, [](NormalProtocolConformance *) {});
32043216
return;
32053217
}
32063218
case MissingWitnessDiagnosisKind::FixItOnly:
@@ -3705,7 +3717,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
37053717
return ResolveWitnessResult::Missing;
37063718
}
37073719

3708-
// Diagnose the error.
3720+
// Diagnose the error.
37093721

37103722
// If there was an invalid witness that might have worked, just
37113723
// suppress the diagnostic entirely. This stops the diagnostic cascade.
@@ -3717,7 +3729,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
37173729

37183730
if (!numViable) {
37193731
// Save the missing requirement for later diagnosis.
3720-
GlobalMissingWitnesses.insert(requirement);
3732+
GlobalMissingWitnesses.insert({requirement, matches});
37213733
diagnoseOrDefer(requirement, true,
37223734
[requirement, matches, nominal](NormalProtocolConformance *conformance) {
37233735
auto dc = conformance->getDeclContext();
@@ -3810,8 +3822,7 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault(
38103822
recordOptionalWitness(requirement);
38113823
return ResolveWitnessResult::Success;
38123824
}
3813-
// Save the missing requirement for later diagnosis.
3814-
GlobalMissingWitnesses.insert(requirement);
3825+
38153826
return ResolveWitnessResult::ExplicitFailed;
38163827
}
38173828

@@ -4036,7 +4047,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
40364047
return ResolveWitnessResult::ExplicitFailed;
40374048
}
40384049
// Save the missing type witness for later diagnosis.
4039-
GlobalMissingWitnesses.insert(assocType);
4050+
GlobalMissingWitnesses.insert({assocType, {}});
40404051

40414052
// None of the candidates were viable.
40424053
diagnoseOrDefer(assocType, true,
@@ -5692,7 +5703,7 @@ TypeWitnessAndDecl
56925703
TypeWitnessRequest::evaluate(Evaluator &eval,
56935704
NormalProtocolConformance *conformance,
56945705
AssociatedTypeDecl *requirement) const {
5695-
llvm::SetVector<ValueDecl*> MissingWitnesses;
5706+
llvm::SetVector<MissingWitness> MissingWitnesses;
56965707
ConformanceChecker checker(requirement->getASTContext(), conformance,
56975708
MissingWitnesses);
56985709
checker.resolveSingleTypeWitness(requirement);
@@ -5710,7 +5721,7 @@ Witness
57105721
ValueWitnessRequest::evaluate(Evaluator &eval,
57115722
NormalProtocolConformance *conformance,
57125723
ValueDecl *requirement) const {
5713-
llvm::SetVector<ValueDecl*> MissingWitnesses;
5724+
llvm::SetVector<MissingWitness> MissingWitnesses;
57145725
ConformanceChecker checker(requirement->getASTContext(), conformance,
57155726
MissingWitnesses);
57165727
checker.resolveSingleWitness(requirement);

lib/Sema/TypeCheckProtocol.h

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,31 @@ enum class MissingWitnessDiagnosisKind {
648648
class AssociatedTypeInference;
649649
class MultiConformanceChecker;
650650

651+
/// Describes a missing witness during conformance checking.
652+
class MissingWitness {
653+
public:
654+
/// The requirement that is missing a witness.
655+
ValueDecl *requirement;
656+
657+
/// The set of potential matching witnesses.
658+
std::vector<RequirementMatch> matches;
659+
660+
MissingWitness(ValueDecl *requirement,
661+
ArrayRef<RequirementMatch> matches)
662+
: requirement(requirement),
663+
matches(matches.begin(), matches.end()) { }
664+
};
665+
666+
/// Capture missing witnesses that have been delayed and will be stored
667+
/// in the ASTContext for later.
668+
class DelayedMissingWitnesses : public MissingWitnessesBase {
669+
public:
670+
std::vector<MissingWitness> missingWitnesses;
671+
672+
DelayedMissingWitnesses(ArrayRef<MissingWitness> missingWitnesses)
673+
: missingWitnesses(missingWitnesses.begin(), missingWitnesses.end()) { }
674+
};
675+
651676
/// The protocol conformance checker.
652677
///
653678
/// This helper class handles most of the details of checking whether a
@@ -670,7 +695,7 @@ class ConformanceChecker : public WitnessChecker {
670695
/// Keep track of missing witnesses, either type or value, for later
671696
/// diagnosis emits. This may contain witnesses that are external to the
672697
/// protocol under checking.
673-
llvm::SetVector<ValueDecl*> &GlobalMissingWitnesses;
698+
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses;
674699

675700
/// Keep track of the slice in GlobalMissingWitnesses that is local to
676701
/// this protocol under checking.
@@ -751,7 +776,7 @@ class ConformanceChecker : public WitnessChecker {
751776
ValueDecl *requirement, bool isError,
752777
std::function<void(NormalProtocolConformance *)> fn);
753778

754-
ArrayRef<ValueDecl*> getLocalMissingWitness() {
779+
ArrayRef<MissingWitness> getLocalMissingWitness() {
755780
return GlobalMissingWitnesses.getArrayRef().
756781
slice(LocalMissingWitnessesStartIndex,
757782
GlobalMissingWitnesses.size() - LocalMissingWitnessesStartIndex);
@@ -769,7 +794,7 @@ class ConformanceChecker : public WitnessChecker {
769794
void emitDelayedDiags();
770795

771796
ConformanceChecker(ASTContext &ctx, NormalProtocolConformance *conformance,
772-
llvm::SetVector<ValueDecl *> &GlobalMissingWitnesses,
797+
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
773798
bool suppressDiagnostics = true);
774799

775800
/// Resolve all of the type witnesses.
@@ -1034,4 +1059,26 @@ void diagnoseConformanceFailure(Type T,
10341059

10351060
}
10361061

1062+
namespace llvm {
1063+
1064+
template<>
1065+
struct DenseMapInfo<swift::MissingWitness> {
1066+
using MissingWitness = swift::MissingWitness;
1067+
using RequirementPointerTraits = DenseMapInfo<swift::ValueDecl *>;
1068+
1069+
static inline MissingWitness getEmptyKey() {
1070+
return MissingWitness(RequirementPointerTraits::getEmptyKey(), {});
1071+
}
1072+
static inline MissingWitness getTombstoneKey() {
1073+
return MissingWitness(RequirementPointerTraits::getTombstoneKey(), {});
1074+
}
1075+
static inline unsigned getHashValue(MissingWitness missing) {
1076+
return RequirementPointerTraits::getHashValue(missing.requirement);
1077+
}
1078+
static bool isEqual(MissingWitness a, MissingWitness b) {
1079+
return a.requirement == b.requirement;
1080+
}
1081+
};
1082+
1083+
}
10371084
#endif // SWIFT_SEMA_PROTOCOL_H

lib/Sema/TypeCheckProtocolInference.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,8 +2014,10 @@ auto AssociatedTypeInference::solve(ConformanceChecker &checker)
20142014
return None;
20152015

20162016
// Save the missing type witnesses for later diagnosis.
2017-
checker.GlobalMissingWitnesses.insert(unresolvedAssocTypes.begin(),
2018-
unresolvedAssocTypes.end());
2017+
for (auto assocType : unresolvedAssocTypes) {
2018+
checker.GlobalMissingWitnesses.insert({assocType, {}});
2019+
}
2020+
20192021
return None;
20202022
}
20212023

0 commit comments

Comments
 (0)