Skip to content

Commit f91767c

Browse files
committed
[Conformance checker] Capture potential matches for missing witnesses.
Rework the data structures we use in the conformance checker to talk about missing witnesses, so they can capture the set of potential matches. This will allow us to delay more diagnostics to later, more uniformly.
1 parent 8dc8532 commit f91767c

File tree

6 files changed

+125
-50
lines changed

6 files changed

+125
-50
lines changed

include/swift/AST/ASTContext.h

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

198198
class SILLayout; // From SIL
199199

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

945955
/// Add delayed missing witnesses for the given normal protocol conformance.
946-
void addDelayedMissingWitnesses(NormalProtocolConformance *conformance,
947-
ArrayRef<ValueDecl*> witnesses);
956+
void addDelayedMissingWitnesses(
957+
NormalProtocolConformance *conformance,
958+
std::unique_ptr<MissingWitnessesBase> missingWitnesses);
948959

949960
/// Retrieve the delayed missing witnesses for the given normal protocol
950961
/// conformance.
951-
std::vector<ValueDecl*>
962+
std::unique_ptr<MissingWitnessesBase>
952963
takeDelayedMissingWitnesses(NormalProtocolConformance *conformance);
953964

954965
/// 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.
@@ -2129,22 +2130,24 @@ bool ASTContext::hasDelayedConformanceErrors() const {
21292130
return false;
21302131
}
21312132

2133+
MissingWitnessesBase::~MissingWitnessesBase() { }
2134+
21322135
void ASTContext::addDelayedConformanceDiag(
21332136
NormalProtocolConformance *conformance,
21342137
DelayedConformanceDiag fn) {
21352138
getImpl().DelayedConformanceDiags[conformance].push_back(std::move(fn));
21362139
}
21372140

2138-
void ASTContext::
2139-
addDelayedMissingWitnesses(NormalProtocolConformance *conformance,
2140-
ArrayRef<ValueDecl*> witnesses) {
2141-
auto &bucket = getImpl().DelayedMissingWitnesses[conformance];
2142-
bucket.insert(bucket.end(), witnesses.begin(), witnesses.end());
2141+
void ASTContext::addDelayedMissingWitnesses(
2142+
NormalProtocolConformance *conformance,
2143+
std::unique_ptr<MissingWitnessesBase> missingWitnesses) {
2144+
getImpl().DelayedMissingWitnesses[conformance] = std::move(missingWitnesses);
21432145
}
21442146

2145-
std::vector<ValueDecl*> ASTContext::
2146-
takeDelayedMissingWitnesses(NormalProtocolConformance *conformance) {
2147-
std::vector<ValueDecl*> result;
2147+
std::unique_ptr<MissingWitnessesBase>
2148+
ASTContext::takeDelayedMissingWitnesses(
2149+
NormalProtocolConformance *conformance) {
2150+
std::unique_ptr<MissingWitnessesBase> result;
21482151
auto known = getImpl().DelayedMissingWitnesses.find(conformance);
21492152
if (known != getImpl().DelayedMissingWitnesses.end()) {
21502153
result = std::move(known->second);

lib/Sema/CSDiagnostics.cpp

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

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

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,7 +1499,7 @@ class swift::MultiConformanceChecker {
14991499
llvm::SmallVector<ValueDecl*, 16> UnsatisfiedReqs;
15001500
llvm::SmallVector<ConformanceChecker, 4> AllUsedCheckers;
15011501
llvm::SmallVector<NormalProtocolConformance*, 4> AllConformances;
1502-
llvm::SetVector<ValueDecl*> MissingWitnesses;
1502+
llvm::SetVector<MissingWitness> MissingWitnesses;
15031503
llvm::SmallPtrSet<ValueDecl *, 8> CoveredMembers;
15041504

15051505
/// Check one conformance.
@@ -1729,13 +1729,17 @@ checkIndividualConformance(NormalProtocolConformance *conformance,
17291729
PrettyStackTraceConformance trace(getASTContext(), "type-checking",
17301730
conformance);
17311731

1732-
std::vector<ValueDecl*> revivedMissingWitnesses;
1732+
std::vector<MissingWitness> revivedMissingWitnesses;
17331733
switch (conformance->getState()) {
17341734
case ProtocolConformanceState::Incomplete:
17351735
if (conformance->isInvalid()) {
17361736
// Revive registered missing witnesses to handle it below.
1737-
revivedMissingWitnesses =
1738-
getASTContext().takeDelayedMissingWitnesses(conformance);
1737+
if (auto delayed = getASTContext().takeDelayedMissingWitnesses(
1738+
conformance)) {
1739+
revivedMissingWitnesses = std::move(
1740+
static_cast<DelayedMissingWitnesses *>(
1741+
delayed.get())->missingWitnesses);
1742+
}
17391743

17401744
// If we have no missing witnesses for this invalid conformance, the
17411745
// conformance is invalid for other reasons, so emit diagnosis now.
@@ -2481,7 +2485,7 @@ diagnoseMatch(ModuleDecl *module, NormalProtocolConformance *conformance,
24812485

24822486
ConformanceChecker::ConformanceChecker(
24832487
ASTContext &ctx, NormalProtocolConformance *conformance,
2484-
llvm::SetVector<ValueDecl *> &GlobalMissingWitnesses,
2488+
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
24852489
bool suppressDiagnostics)
24862490
: WitnessChecker(ctx, conformance->getProtocol(), conformance->getType(),
24872491
conformance->getDeclContext()),
@@ -2986,26 +2990,28 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
29862990
/// NoStubRequirements.
29872991
static void
29882992
printProtocolStubFixitString(SourceLoc TypeLoc, ProtocolConformance *Conf,
2989-
ArrayRef<ValueDecl*> MissingWitnesses,
2993+
ArrayRef<MissingWitness> MissingWitnesses,
29902994
std::string &FixitString,
29912995
llvm::SetVector<ValueDecl*> &NoStubRequirements) {
29922996
llvm::raw_string_ostream FixitStream(FixitString);
29932997
std::for_each(MissingWitnesses.begin(), MissingWitnesses.end(),
2994-
[&](ValueDecl* VD) {
2995-
if (!printRequirementStub(VD, Conf->getDeclContext(), Conf->getType(),
2996-
TypeLoc, FixitStream)) {
2997-
NoStubRequirements.insert(VD);
2998+
[&](const MissingWitness &Missing) {
2999+
if (!printRequirementStub(
3000+
Missing.requirement, Conf->getDeclContext(), Conf->getType(),
3001+
TypeLoc, FixitStream)) {
3002+
NoStubRequirements.insert(Missing.requirement);
29983003
}
29993004
});
30003005
}
30013006

30023007
/// Filter the given array of protocol requirements and produce a new vector
30033008
/// containing the non-conflicting requirements to be implemented by the given
30043009
/// \c Adoptee type.
3005-
static llvm::SmallVector<ValueDecl *, 4>
3006-
filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
3007-
llvm::SmallVector<ValueDecl *, 4> Filtered;
3008-
if (Reqs.empty()) {
3010+
static llvm::SmallVector<MissingWitness, 4>
3011+
filterProtocolRequirements(
3012+
ArrayRef<MissingWitness> MissingWitnesses, Type Adoptee) {
3013+
llvm::SmallVector<MissingWitness, 4> Filtered;
3014+
if (MissingWitnesses.empty()) {
30093015
return Filtered;
30103016
}
30113017

@@ -3017,10 +3023,11 @@ filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
30173023

30183024
llvm::SmallDenseMap<DeclName, llvm::SmallVector<ValueDecl *, 2>, 4>
30193025
DeclsByName;
3020-
for (auto *const Req : Reqs) {
3026+
for (const auto &Missing: MissingWitnesses) {
3027+
auto Req = Missing.requirement;
30213028
if (DeclsByName.find(Req->getName()) == DeclsByName.end()) {
30223029
DeclsByName[Req->getName()] = {Req};
3023-
Filtered.push_back(Req);
3030+
Filtered.push_back(Missing);
30243031
continue;
30253032
}
30263033

@@ -3046,7 +3053,7 @@ filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
30463053
}
30473054

30483055
DeclsByName[Req->getName()].push_back(Req);
3049-
Filtered.push_back(Req);
3056+
Filtered.push_back(Missing);
30503057
}
30513058

30523059
return Filtered;
@@ -3060,10 +3067,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
30603067
if (LocalMissing.empty())
30613068
return;
30623069

3063-
const auto InsertFixit = [](NormalProtocolConformance *Conf,
3064-
SourceLoc ComplainLoc, bool EditorMode,
3065-
llvm::SmallVector<ValueDecl *, 4>
3066-
MissingWitnesses) {
3070+
const auto InsertFixit = [](
3071+
NormalProtocolConformance *Conf, SourceLoc ComplainLoc, bool EditorMode,
3072+
llvm::SmallVector<MissingWitness, 4> MissingWitnesses) {
30673073
DeclContext *DC = Conf->getDeclContext();
30683074
// The location where to insert stubs.
30693075
SourceLoc FixitLocation;
@@ -3097,7 +3103,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
30973103
}
30983104
auto &SM = DC->getASTContext().SourceMgr;
30993105
auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation);
3100-
for (auto VD : MissingWitnesses) {
3106+
for (const auto &Missing : MissingWitnesses) {
3107+
auto VD = Missing.requirement;
3108+
31013109
// Don't ever emit a diagnostic for a requirement in the NSObject
31023110
// protocol. They're not implementable.
31033111
if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl()))
@@ -3167,18 +3175,22 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
31673175
// If the diagnostics are suppressed, we register these missing witnesses
31683176
// for later revisiting.
31693177
Conformance->setInvalid();
3170-
getASTContext().addDelayedMissingWitnesses(Conformance, MissingWitnesses);
3178+
getASTContext().addDelayedMissingWitnesses(
3179+
Conformance,
3180+
std::make_unique<DelayedMissingWitnesses>(MissingWitnesses));
31713181
} else {
31723182
diagnoseOrDefer(
3173-
LocalMissing[0], true, [&](NormalProtocolConformance *Conf) {
3183+
LocalMissing[0].requirement, true,
3184+
[&](NormalProtocolConformance *Conf) {
31743185
InsertFixit(Conf, Loc, IsEditorMode, std::move(MissingWitnesses));
31753186
});
31763187
}
31773188
clearGlobalMissingWitnesses();
31783189
return;
31793190
}
31803191
case MissingWitnessDiagnosisKind::ErrorOnly: {
3181-
diagnoseOrDefer(LocalMissing[0], true, [](NormalProtocolConformance *) {});
3192+
diagnoseOrDefer(
3193+
LocalMissing[0].requirement, true, [](NormalProtocolConformance *) {});
31823194
return;
31833195
}
31843196
case MissingWitnessDiagnosisKind::FixItOnly:
@@ -3683,7 +3695,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
36833695
return ResolveWitnessResult::Missing;
36843696
}
36853697

3686-
// Diagnose the error.
3698+
// Diagnose the error.
36873699

36883700
// If there was an invalid witness that might have worked, just
36893701
// suppress the diagnostic entirely. This stops the diagnostic cascade.
@@ -3695,7 +3707,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
36953707

36963708
if (!numViable) {
36973709
// Save the missing requirement for later diagnosis.
3698-
GlobalMissingWitnesses.insert(requirement);
3710+
GlobalMissingWitnesses.insert({requirement, matches});
36993711
diagnoseOrDefer(requirement, true,
37003712
[requirement, matches, nominal](NormalProtocolConformance *conformance) {
37013713
auto dc = conformance->getDeclContext();
@@ -3789,7 +3801,7 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault(
37893801
return ResolveWitnessResult::Success;
37903802
}
37913803
// Save the missing requirement for later diagnosis.
3792-
GlobalMissingWitnesses.insert(requirement);
3804+
GlobalMissingWitnesses.insert({requirement, {}});
37933805
return ResolveWitnessResult::ExplicitFailed;
37943806
}
37953807

@@ -4014,7 +4026,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
40144026
return ResolveWitnessResult::ExplicitFailed;
40154027
}
40164028
// Save the missing type witness for later diagnosis.
4017-
GlobalMissingWitnesses.insert(assocType);
4029+
GlobalMissingWitnesses.insert({assocType, {}});
40184030

40194031
// None of the candidates were viable.
40204032
diagnoseOrDefer(assocType, true,
@@ -5670,7 +5682,7 @@ TypeWitnessAndDecl
56705682
TypeWitnessRequest::evaluate(Evaluator &eval,
56715683
NormalProtocolConformance *conformance,
56725684
AssociatedTypeDecl *requirement) const {
5673-
llvm::SetVector<ValueDecl*> MissingWitnesses;
5685+
llvm::SetVector<MissingWitness> MissingWitnesses;
56745686
ConformanceChecker checker(requirement->getASTContext(), conformance,
56755687
MissingWitnesses);
56765688
checker.resolveSingleTypeWitness(requirement);
@@ -5688,7 +5700,7 @@ Witness
56885700
ValueWitnessRequest::evaluate(Evaluator &eval,
56895701
NormalProtocolConformance *conformance,
56905702
ValueDecl *requirement) const {
5691-
llvm::SetVector<ValueDecl*> MissingWitnesses;
5703+
llvm::SetVector<MissingWitness> MissingWitnesses;
56925704
ConformanceChecker checker(requirement->getASTContext(), conformance,
56935705
MissingWitnesses);
56945706
checker.resolveSingleWitness(requirement);

lib/Sema/TypeCheckProtocol.h

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,31 @@ enum class MissingWitnessDiagnosisKind {
643643
class AssociatedTypeInference;
644644
class MultiConformanceChecker;
645645

646+
/// Describes a missing witness during conformance checking.
647+
class MissingWitness {
648+
public:
649+
/// The requirement that is missing a witness.
650+
ValueDecl *requirement;
651+
652+
/// The set of potential matching witnesses.
653+
std::vector<RequirementMatch> matches;
654+
655+
MissingWitness(ValueDecl *requirement,
656+
ArrayRef<RequirementMatch> matches)
657+
: requirement(requirement),
658+
matches(matches.begin(), matches.end()) { }
659+
};
660+
661+
/// Capture missing witnesses that have been delayed and will be stored
662+
/// in the ASTContext for later.
663+
class DelayedMissingWitnesses : public MissingWitnessesBase {
664+
public:
665+
std::vector<MissingWitness> missingWitnesses;
666+
667+
DelayedMissingWitnesses(ArrayRef<MissingWitness> missingWitnesses)
668+
: missingWitnesses(missingWitnesses.begin(), missingWitnesses.end()) { }
669+
};
670+
646671
/// The protocol conformance checker.
647672
///
648673
/// This helper class handles most of the details of checking whether a
@@ -665,7 +690,7 @@ class ConformanceChecker : public WitnessChecker {
665690
/// Keep track of missing witnesses, either type or value, for later
666691
/// diagnosis emits. This may contain witnesses that are external to the
667692
/// protocol under checking.
668-
llvm::SetVector<ValueDecl*> &GlobalMissingWitnesses;
693+
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses;
669694

670695
/// Keep track of the slice in GlobalMissingWitnesses that is local to
671696
/// this protocol under checking.
@@ -746,7 +771,7 @@ class ConformanceChecker : public WitnessChecker {
746771
ValueDecl *requirement, bool isError,
747772
std::function<void(NormalProtocolConformance *)> fn);
748773

749-
ArrayRef<ValueDecl*> getLocalMissingWitness() {
774+
ArrayRef<MissingWitness> getLocalMissingWitness() {
750775
return GlobalMissingWitnesses.getArrayRef().
751776
slice(LocalMissingWitnessesStartIndex,
752777
GlobalMissingWitnesses.size() - LocalMissingWitnessesStartIndex);
@@ -764,7 +789,7 @@ class ConformanceChecker : public WitnessChecker {
764789
void emitDelayedDiags();
765790

766791
ConformanceChecker(ASTContext &ctx, NormalProtocolConformance *conformance,
767-
llvm::SetVector<ValueDecl *> &GlobalMissingWitnesses,
792+
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
768793
bool suppressDiagnostics = true);
769794

770795
/// Resolve all of the type witnesses.
@@ -1030,4 +1055,26 @@ void diagnoseConformanceFailure(Type T,
10301055

10311056
}
10321057

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

lib/Sema/TypeCheckProtocolInference.cpp

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

20122012
// Save the missing type witnesses for later diagnosis.
2013-
checker.GlobalMissingWitnesses.insert(unresolvedAssocTypes.begin(),
2014-
unresolvedAssocTypes.end());
2013+
for (auto assocType : unresolvedAssocTypes) {
2014+
checker.GlobalMissingWitnesses.insert({assocType, {}});
2015+
}
2016+
20152017
return None;
20162018
}
20172019

0 commit comments

Comments
 (0)