Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@ class ConstraintCheckerArenaRAII {

class SILLayout; // From SIL

/// A set of missing witnesses for a given conformance. These are temporarily
/// stashed in the ASTContext so the type checker can get at them.
///
/// The only subclass is owned by the type checker, so it can hide its own
/// data structures.
class MissingWitnessesBase {
public:
virtual ~MissingWitnessesBase();
};

/// ASTContext - This object creates and owns the AST objects.
/// However, this class does more than just maintain context within an AST.
/// It is the closest thing to thread-local or compile-local storage in this
Expand Down Expand Up @@ -943,12 +953,13 @@ class ASTContext final {
takeDelayedConformanceDiags(NormalProtocolConformance *conformance);

/// Add delayed missing witnesses for the given normal protocol conformance.
void addDelayedMissingWitnesses(NormalProtocolConformance *conformance,
ArrayRef<ValueDecl*> witnesses);
void addDelayedMissingWitnesses(
NormalProtocolConformance *conformance,
std::unique_ptr<MissingWitnessesBase> missingWitnesses);

/// Retrieve the delayed missing witnesses for the given normal protocol
/// conformance.
std::vector<ValueDecl*>
std::unique_ptr<MissingWitnessesBase>
takeDelayedMissingWitnesses(NormalProtocolConformance *conformance);

/// Produce a specialized conformance, which takes a generic
Expand Down
21 changes: 12 additions & 9 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ struct ASTContext::Implementation {
/// Map from normal protocol conformances to missing witnesses that have
/// been delayed until the conformance is fully checked, so that we can
/// issue a fixit that fills the entire protocol stub.
llvm::DenseMap<NormalProtocolConformance *, std::vector<ValueDecl*>>
llvm::DenseMap<
NormalProtocolConformance *, std::unique_ptr<MissingWitnessesBase>>
DelayedMissingWitnesses;

/// Stores information about lazy deserialization of various declarations.
Expand Down Expand Up @@ -2129,22 +2130,24 @@ bool ASTContext::hasDelayedConformanceErrors() const {
return false;
}

MissingWitnessesBase::~MissingWitnessesBase() { }

void ASTContext::addDelayedConformanceDiag(
NormalProtocolConformance *conformance,
DelayedConformanceDiag fn) {
getImpl().DelayedConformanceDiags[conformance].push_back(std::move(fn));
}

void ASTContext::
addDelayedMissingWitnesses(NormalProtocolConformance *conformance,
ArrayRef<ValueDecl*> witnesses) {
auto &bucket = getImpl().DelayedMissingWitnesses[conformance];
bucket.insert(bucket.end(), witnesses.begin(), witnesses.end());
void ASTContext::addDelayedMissingWitnesses(
NormalProtocolConformance *conformance,
std::unique_ptr<MissingWitnessesBase> missingWitnesses) {
getImpl().DelayedMissingWitnesses[conformance] = std::move(missingWitnesses);
}

std::vector<ValueDecl*> ASTContext::
takeDelayedMissingWitnesses(NormalProtocolConformance *conformance) {
std::vector<ValueDecl*> result;
std::unique_ptr<MissingWitnessesBase>
ASTContext::takeDelayedMissingWitnesses(
NormalProtocolConformance *conformance) {
std::unique_ptr<MissingWitnessesBase> result;
auto known = getImpl().DelayedMissingWitnesses.find(conformance);
if (known != getImpl().DelayedMissingWitnesses.end()) {
result = std::move(known->second);
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2864,7 +2864,7 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
{
llvm::SmallString<128> Text;
llvm::raw_svector_ostream SS(Text);
llvm::SetVector<ValueDecl *> missingWitnesses;
llvm::SetVector<MissingWitness> missingWitnesses;
for (auto protocol : missingProtocols) {
auto conformance = NormalProtocolConformance(
nominal->getDeclaredType(), protocol, SourceLoc(), nominal,
Expand All @@ -2876,7 +2876,7 @@ bool ContextualFailure::tryProtocolConformanceFixIt(
}

for (auto decl : missingWitnesses) {
swift::printRequirementStub(decl, nominal, nominal->getDeclaredType(),
swift::printRequirementStub(decl.requirement, nominal, nominal->getDeclaredType(),
nominal->getStartLoc(), SS);
}

Expand Down
75 changes: 43 additions & 32 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1499,7 +1499,7 @@ class swift::MultiConformanceChecker {
llvm::SmallVector<ValueDecl*, 16> UnsatisfiedReqs;
llvm::SmallVector<ConformanceChecker, 4> AllUsedCheckers;
llvm::SmallVector<NormalProtocolConformance*, 4> AllConformances;
llvm::SetVector<ValueDecl*> MissingWitnesses;
llvm::SetVector<MissingWitness> MissingWitnesses;
llvm::SmallPtrSet<ValueDecl *, 8> CoveredMembers;

/// Check one conformance.
Expand Down Expand Up @@ -1729,13 +1729,17 @@ checkIndividualConformance(NormalProtocolConformance *conformance,
PrettyStackTraceConformance trace(getASTContext(), "type-checking",
conformance);

std::vector<ValueDecl*> revivedMissingWitnesses;
std::vector<MissingWitness> revivedMissingWitnesses;
switch (conformance->getState()) {
case ProtocolConformanceState::Incomplete:
if (conformance->isInvalid()) {
// Revive registered missing witnesses to handle it below.
revivedMissingWitnesses =
getASTContext().takeDelayedMissingWitnesses(conformance);
if (auto delayed = getASTContext().takeDelayedMissingWitnesses(
conformance)) {
revivedMissingWitnesses = std::move(
static_cast<DelayedMissingWitnesses *>(
delayed.get())->missingWitnesses);
}

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

ConformanceChecker::ConformanceChecker(
ASTContext &ctx, NormalProtocolConformance *conformance,
llvm::SetVector<ValueDecl *> &GlobalMissingWitnesses,
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
bool suppressDiagnostics)
: WitnessChecker(ctx, conformance->getProtocol(), conformance->getType(),
conformance->getDeclContext()),
Expand Down Expand Up @@ -2986,26 +2990,28 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
/// NoStubRequirements.
static void
printProtocolStubFixitString(SourceLoc TypeLoc, ProtocolConformance *Conf,
ArrayRef<ValueDecl*> MissingWitnesses,
ArrayRef<MissingWitness> MissingWitnesses,
std::string &FixitString,
llvm::SetVector<ValueDecl*> &NoStubRequirements) {
llvm::raw_string_ostream FixitStream(FixitString);
std::for_each(MissingWitnesses.begin(), MissingWitnesses.end(),
[&](ValueDecl* VD) {
if (!printRequirementStub(VD, Conf->getDeclContext(), Conf->getType(),
TypeLoc, FixitStream)) {
NoStubRequirements.insert(VD);
[&](const MissingWitness &Missing) {
if (!printRequirementStub(
Missing.requirement, Conf->getDeclContext(), Conf->getType(),
TypeLoc, FixitStream)) {
NoStubRequirements.insert(Missing.requirement);
}
});
}

/// Filter the given array of protocol requirements and produce a new vector
/// containing the non-conflicting requirements to be implemented by the given
/// \c Adoptee type.
static llvm::SmallVector<ValueDecl *, 4>
filterProtocolRequirements(ArrayRef<ValueDecl *> Reqs, Type Adoptee) {
llvm::SmallVector<ValueDecl *, 4> Filtered;
if (Reqs.empty()) {
static llvm::SmallVector<MissingWitness, 4>
filterProtocolRequirements(
ArrayRef<MissingWitness> MissingWitnesses, Type Adoptee) {
llvm::SmallVector<MissingWitness, 4> Filtered;
if (MissingWitnesses.empty()) {
return Filtered;
}

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

llvm::SmallDenseMap<DeclName, llvm::SmallVector<ValueDecl *, 2>, 4>
DeclsByName;
for (auto *const Req : Reqs) {
for (const auto &Missing: MissingWitnesses) {
auto Req = Missing.requirement;
if (DeclsByName.find(Req->getName()) == DeclsByName.end()) {
DeclsByName[Req->getName()] = {Req};
Filtered.push_back(Req);
Filtered.push_back(Missing);
continue;
}

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

DeclsByName[Req->getName()].push_back(Req);
Filtered.push_back(Req);
Filtered.push_back(Missing);
}

return Filtered;
Expand All @@ -3060,10 +3067,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
if (LocalMissing.empty())
return;

const auto InsertFixit = [](NormalProtocolConformance *Conf,
SourceLoc ComplainLoc, bool EditorMode,
llvm::SmallVector<ValueDecl *, 4>
MissingWitnesses) {
const auto InsertFixit = [](
NormalProtocolConformance *Conf, SourceLoc ComplainLoc, bool EditorMode,
llvm::SmallVector<MissingWitness, 4> MissingWitnesses) {
DeclContext *DC = Conf->getDeclContext();
// The location where to insert stubs.
SourceLoc FixitLocation;
Expand Down Expand Up @@ -3097,7 +3103,9 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
}
auto &SM = DC->getASTContext().SourceMgr;
auto FixitBufferId = SM.findBufferContainingLoc(FixitLocation);
for (auto VD : MissingWitnesses) {
for (const auto &Missing : MissingWitnesses) {
auto VD = Missing.requirement;

// Don't ever emit a diagnostic for a requirement in the NSObject
// protocol. They're not implementable.
if (isNSObjectProtocol(VD->getDeclContext()->getSelfProtocolDecl()))
Expand Down Expand Up @@ -3167,18 +3175,22 @@ diagnoseMissingWitnesses(MissingWitnessDiagnosisKind Kind) {
// If the diagnostics are suppressed, we register these missing witnesses
// for later revisiting.
Conformance->setInvalid();
getASTContext().addDelayedMissingWitnesses(Conformance, MissingWitnesses);
getASTContext().addDelayedMissingWitnesses(
Conformance,
std::make_unique<DelayedMissingWitnesses>(MissingWitnesses));
} else {
diagnoseOrDefer(
LocalMissing[0], true, [&](NormalProtocolConformance *Conf) {
LocalMissing[0].requirement, true,
[&](NormalProtocolConformance *Conf) {
InsertFixit(Conf, Loc, IsEditorMode, std::move(MissingWitnesses));
});
}
clearGlobalMissingWitnesses();
return;
}
case MissingWitnessDiagnosisKind::ErrorOnly: {
diagnoseOrDefer(LocalMissing[0], true, [](NormalProtocolConformance *) {});
diagnoseOrDefer(
LocalMissing[0].requirement, true, [](NormalProtocolConformance *) {});
return;
}
case MissingWitnessDiagnosisKind::FixItOnly:
Expand Down Expand Up @@ -3683,7 +3695,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
return ResolveWitnessResult::Missing;
}

// Diagnose the error.
// Diagnose the error.

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

if (!numViable) {
// Save the missing requirement for later diagnosis.
GlobalMissingWitnesses.insert(requirement);
GlobalMissingWitnesses.insert({requirement, matches});
diagnoseOrDefer(requirement, true,
[requirement, matches, nominal](NormalProtocolConformance *conformance) {
auto dc = conformance->getDeclContext();
Expand Down Expand Up @@ -3788,8 +3800,7 @@ ResolveWitnessResult ConformanceChecker::resolveWitnessViaDefault(
recordOptionalWitness(requirement);
return ResolveWitnessResult::Success;
}
// Save the missing requirement for later diagnosis.
GlobalMissingWitnesses.insert(requirement);

return ResolveWitnessResult::ExplicitFailed;
}

Expand Down Expand Up @@ -4014,7 +4025,7 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
return ResolveWitnessResult::ExplicitFailed;
}
// Save the missing type witness for later diagnosis.
GlobalMissingWitnesses.insert(assocType);
GlobalMissingWitnesses.insert({assocType, {}});

// None of the candidates were viable.
diagnoseOrDefer(assocType, true,
Expand Down Expand Up @@ -5670,7 +5681,7 @@ TypeWitnessAndDecl
TypeWitnessRequest::evaluate(Evaluator &eval,
NormalProtocolConformance *conformance,
AssociatedTypeDecl *requirement) const {
llvm::SetVector<ValueDecl*> MissingWitnesses;
llvm::SetVector<MissingWitness> MissingWitnesses;
ConformanceChecker checker(requirement->getASTContext(), conformance,
MissingWitnesses);
checker.resolveSingleTypeWitness(requirement);
Expand All @@ -5688,7 +5699,7 @@ Witness
ValueWitnessRequest::evaluate(Evaluator &eval,
NormalProtocolConformance *conformance,
ValueDecl *requirement) const {
llvm::SetVector<ValueDecl*> MissingWitnesses;
llvm::SetVector<MissingWitness> MissingWitnesses;
ConformanceChecker checker(requirement->getASTContext(), conformance,
MissingWitnesses);
checker.resolveSingleWitness(requirement);
Expand Down
53 changes: 50 additions & 3 deletions lib/Sema/TypeCheckProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,31 @@ enum class MissingWitnessDiagnosisKind {
class AssociatedTypeInference;
class MultiConformanceChecker;

/// Describes a missing witness during conformance checking.
class MissingWitness {
public:
/// The requirement that is missing a witness.
ValueDecl *requirement;

/// The set of potential matching witnesses.
std::vector<RequirementMatch> matches;

MissingWitness(ValueDecl *requirement,
ArrayRef<RequirementMatch> matches)
: requirement(requirement),
matches(matches.begin(), matches.end()) { }
};

/// Capture missing witnesses that have been delayed and will be stored
/// in the ASTContext for later.
class DelayedMissingWitnesses : public MissingWitnessesBase {
public:
std::vector<MissingWitness> missingWitnesses;

DelayedMissingWitnesses(ArrayRef<MissingWitness> missingWitnesses)
: missingWitnesses(missingWitnesses.begin(), missingWitnesses.end()) { }
};

/// The protocol conformance checker.
///
/// This helper class handles most of the details of checking whether a
Expand All @@ -665,7 +690,7 @@ class ConformanceChecker : public WitnessChecker {
/// Keep track of missing witnesses, either type or value, for later
/// diagnosis emits. This may contain witnesses that are external to the
/// protocol under checking.
llvm::SetVector<ValueDecl*> &GlobalMissingWitnesses;
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses;

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

ArrayRef<ValueDecl*> getLocalMissingWitness() {
ArrayRef<MissingWitness> getLocalMissingWitness() {
return GlobalMissingWitnesses.getArrayRef().
slice(LocalMissingWitnessesStartIndex,
GlobalMissingWitnesses.size() - LocalMissingWitnessesStartIndex);
Expand All @@ -764,7 +789,7 @@ class ConformanceChecker : public WitnessChecker {
void emitDelayedDiags();

ConformanceChecker(ASTContext &ctx, NormalProtocolConformance *conformance,
llvm::SetVector<ValueDecl *> &GlobalMissingWitnesses,
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
bool suppressDiagnostics = true);

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

}

namespace llvm {

template<>
struct DenseMapInfo<swift::MissingWitness> {
using MissingWitness = swift::MissingWitness;
using RequirementPointerTraits = DenseMapInfo<swift::ValueDecl *>;

static inline MissingWitness getEmptyKey() {
return MissingWitness(RequirementPointerTraits::getEmptyKey(), {});
}
static inline MissingWitness getTombstoneKey() {
return MissingWitness(RequirementPointerTraits::getTombstoneKey(), {});
}
static inline unsigned getHashValue(MissingWitness missing) {
return RequirementPointerTraits::getHashValue(missing.requirement);
}
static bool isEqual(MissingWitness a, MissingWitness b) {
return a.requirement == b.requirement;
}
};

}
#endif // SWIFT_SEMA_PROTOCOL_H
6 changes: 4 additions & 2 deletions lib/Sema/TypeCheckProtocolInference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2010,8 +2010,10 @@ auto AssociatedTypeInference::solve(ConformanceChecker &checker)
return None;

// Save the missing type witnesses for later diagnosis.
checker.GlobalMissingWitnesses.insert(unresolvedAssocTypes.begin(),
unresolvedAssocTypes.end());
for (auto assocType : unresolvedAssocTypes) {
checker.GlobalMissingWitnesses.insert({assocType, {}});
}

return None;
}

Expand Down