Skip to content

Sema: Decouple type witness resolution from the ConformanceChecker #71131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4d7a223
Fix typo in name of test file
slavapestov Jan 24, 2024
d4d8e35
Sema: Clean up checkIndividualConformance()
slavapestov Jan 23, 2024
8c4b651
Sema: Pull getObjCRequirementSibling() and friends out of Conformance…
slavapestov Jan 23, 2024
18c8148
Sema: Move some more code around
slavapestov Jan 23, 2024
6306884
Sema: Only call diagnoseMissingWitnesses() once
slavapestov Jan 23, 2024
c347b3e
AST: Factor out maybeEmitFallbackConformanceDiagnostic() from addDela…
slavapestov Jan 23, 2024
35b3f9b
AST: Emit fallback diagnostic when a missing witness is recorded
slavapestov Jan 23, 2024
ec7269d
Sema: Clean up diagnoseMissingWitnesses()
slavapestov Jan 23, 2024
9c077ec
Sema: Consolidate calls to emitDelayedDiags()
slavapestov Jan 24, 2024
516662b
Sema: Clean up dodgy missing witness fixit logic
slavapestov Jan 24, 2024
7c4708f
Sema: Introduce shouldRecordMissingWitness()
slavapestov Jan 23, 2024
f2c27df
Sema: Remove pruneMissingWitnesses()
slavapestov Jan 24, 2024
60801bf
Sema: Split up ConformanceChecker::diagnoseMissingWitnesses()
slavapestov Jan 24, 2024
9831b0c
Sema: Stop recording missing witnesses in the ConformanceChecker
slavapestov Jan 24, 2024
7caf193
Sema: AssociatedTypeInference::solve() doesn't need a ConformanceChec…
slavapestov Jan 24, 2024
2764333
Sema: Introduce ResolveTypeWitnessesRequest
slavapestov Jan 24, 2024
4b073b8
Sema: Remove MultiConformanceChecker::AllUsedCheckers
slavapestov Jan 24, 2024
5645539
Sema: Pull ensureRequirementsAreSatisfied() out of ConformanceChecker
slavapestov Jan 24, 2024
979fc28
AST: Remove ProtocolConformanceState::CheckingTypeWitnesses
slavapestov Jan 24, 2024
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
3 changes: 0 additions & 3 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ enum class ProtocolConformanceState {
Complete = 0,
/// The conformance is known but is not yet complete.
Incomplete,
/// The conformance's type witnesses are currently being resolved.
CheckingTypeWitnesses,
/// The conformance is being checked.
Checking,

Expand Down Expand Up @@ -225,7 +223,6 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance
/// Determine whether this conformance is incomplete.
bool isIncomplete() const {
return getState() == ProtocolConformanceState::Incomplete ||
getState() == ProtocolConformanceState::CheckingTypeWitnesses ||
getState() == ProtocolConformanceState::Checking;
}

Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,24 @@ class ReferencedAssociatedTypesRequest
bool isCached() const { return true; }
};

class ResolveTypeWitnessesRequest
: public SimpleRequest<ResolveTypeWitnessesRequest,
evaluator::SideEffect(NormalProtocolConformance *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
evaluator::SideEffect
evaluate(Evaluator &evaluator, NormalProtocolConformance *conformance) const;

public:
bool isCached() const { return true; }
};

class ValueWitnessRequest
: public SimpleRequest<ValueWitnessRequest,
Witness(NormalProtocolConformance *, ValueDecl *),
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ SWIFT_REQUEST(TypeChecker, AssociatedConformanceRequest,
SWIFT_REQUEST(TypeChecker, ReferencedAssociatedTypesRequest,
TinyPtrVector<AssociatedTypeDecl *>(ValueDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ResolveTypeWitnessesRequest,
evaluator::SideEffect(NormalProtocolConformance *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ValueWitnessRequest,
Witness(NormalProtocolConformance *, ValueDecl *),
SeparatelyCached, NoLocationInfo)
Expand Down
79 changes: 46 additions & 33 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2780,56 +2780,69 @@ ASTContext::MissingWitness::MissingWitness(ValueDecl *requirement,
: requirement(requirement),
matches(matches.begin(), matches.end()) { }

void ASTContext::addDelayedConformanceDiag(
NormalProtocolConformance *conformance, bool isError,
std::function<void(NormalProtocolConformance *)> callback) {
if (isError)
conformance->setInvalid();

auto &diagnostics = getImpl().DelayedConformanceDiags[conformance];
static void maybeEmitFallbackConformanceDiagnostic(
ASTContext &ctx,
NormalProtocolConformance *conformance,
DelayedConformanceDiags &diagnostics) {

if (isError && !diagnostics.HadError) {
diagnostics.HadError = true;
if (diagnostics.HadError)
return;

auto *proto = conformance->getProtocol();
auto *dc = conformance->getDeclContext();
auto *sf = dc->getParentSourceFile();
auto *mod = sf->getParentModule();
assert(mod->isMainModule());
diagnostics.HadError = true;

// If we have at least one primary file and the conformance is declared in a
// non-primary file, emit a fallback diagnostic.
if ((!sf->isPrimary() && !mod->getPrimarySourceFiles().empty()) ||
TypeCheckerOpts.EnableLazyTypecheck) {
auto complainLoc = evaluator.getInnermostSourceLoc([&](SourceLoc loc) {
if (loc.isInvalid())
return false;
auto *proto = conformance->getProtocol();
auto *dc = conformance->getDeclContext();
auto *sf = dc->getParentSourceFile();
auto *mod = sf->getParentModule();
assert(mod->isMainModule());

auto *otherSF = mod->getSourceFileContainingLocation(loc);
if (otherSF == nullptr)
return false;
// If we have at least one primary file and the conformance is declared in a
// non-primary file, emit a fallback diagnostic.
if ((!sf->isPrimary() && !mod->getPrimarySourceFiles().empty()) ||
ctx.TypeCheckerOpts.EnableLazyTypecheck) {
auto complainLoc = ctx.evaluator.getInnermostSourceLoc([&](SourceLoc loc) {
if (loc.isInvalid())
return false;

return otherSF->isPrimary();
});
auto *otherSF = mod->getSourceFileContainingLocation(loc);
if (otherSF == nullptr)
return false;

if (complainLoc.isInvalid()) {
complainLoc = conformance->getLoc();
}
return otherSF->isPrimary();
});

Diags.diagnose(complainLoc,
diag::type_does_not_conform,
dc->getSelfInterfaceType(),
proto->getDeclaredInterfaceType());
if (complainLoc.isInvalid()) {
complainLoc = conformance->getLoc();
}

ctx.Diags.diagnose(complainLoc,
diag::type_does_not_conform,
dc->getSelfInterfaceType(),
proto->getDeclaredInterfaceType());
}
}

void ASTContext::addDelayedConformanceDiag(
NormalProtocolConformance *conformance, bool isError,
std::function<void(NormalProtocolConformance *)> callback) {
if (isError)
conformance->setInvalid();

auto &diagnostics = getImpl().DelayedConformanceDiags[conformance];

if (isError)
maybeEmitFallbackConformanceDiagnostic(*this, conformance, diagnostics);

diagnostics.Diags.push_back({isError, callback});
}

void ASTContext::addDelayedMissingWitness(
NormalProtocolConformance *conformance,
ASTContext::MissingWitness missingWitness) {
conformance->setInvalid();

auto &diagnostics = getImpl().DelayedConformanceDiags[conformance];
maybeEmitFallbackConformanceDiagnostic(*this, conformance, diagnostics);
diagnostics.MissingWitnesses.push_back(missingWitness);
}

Expand Down
1 change: 0 additions & 1 deletion lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2803,7 +2803,6 @@ class Verifier : public ASTWalker {
// Ignore incomplete conformances; we didn't need them.
return;

case ProtocolConformanceState::CheckingTypeWitnesses:
case ProtocolConformanceState::Checking:
dumpRef(decl);
Out << " has a protocol conformance that is still being checked "
Expand Down
7 changes: 6 additions & 1 deletion lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,12 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,

// If this conformance is in a state where it is inferring type witnesses but
// we didn't find anything, fail.
if (getState() == ProtocolConformanceState::CheckingTypeWitnesses) {
//
// FIXME: This is unsound, because we may not have diagnosed anything but
// still end up with an ErrorType in the AST.
if (getDeclContext()->getASTContext().evaluator.hasActiveRequest(
ResolveTypeWitnessesRequest{
const_cast<NormalProtocolConformance *>(this)})) {
return { Type(), nullptr };
}

Expand Down
9 changes: 6 additions & 3 deletions lib/AST/SubstitutionMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,13 @@ SubstitutionMap::lookupConformance(CanType type, ProtocolDecl *proto) const {
if (!normal->hasComputedAssociatedConformances()) {
// If we're in the process of checking the type witnesses, fail
// gracefully.
// FIXME: Seems like we should be able to get at the intermediate state
// to use that.
if (normal->getState() == ProtocolConformanceState::CheckingTypeWitnesses)
//
// FIXME: This is unsound, because we may not have diagnosed anything but
// still end up with an ErrorType in the AST.
if (proto->getASTContext().evaluator.hasActiveRequest(
ResolveTypeWitnessesRequest{normal})) {
return ProtocolConformanceRef::forInvalid();
}
}

// Get the associated conformance.
Expand Down
Loading