Skip to content

Commit e2c3e96

Browse files
committed
flush delayed diagnostics in dtor of MultiConformanceChecker
While individual ConformanceCheckers tend to be used for one-off conformance queries where diagnostics are fully suppressed, the MultiConformanceChecker is a major driver of overall protocol conformance checking and should not be losing delayed error diags. In some situations (mostly with checking for conformance to ObjC protocols that have async-like requirements), a delayed error diagnostic can be lost by the MultiConformanceChecker, because the deferred diag was added after the calls to emit them happen. This new flush operation in MultiConformanceChecker's destructor fixes the issue in those situations. Additionally, I attempted to try and catch mistakes like this in the future by adding an assert to ConformanceChecker's dtor for obviously lost error diagnostics. resolves rdar://73641790
1 parent b16f340 commit e2c3e96

File tree

4 files changed

+51
-10
lines changed

4 files changed

+51
-10
lines changed

include/swift/AST/ASTContext.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,13 @@ class ASTContext final {
986986

987987
/// Check whether current context has any errors associated with
988988
/// ill-formed protocol conformances which haven't been produced yet.
989-
bool hasDelayedConformanceErrors() const;
989+
///
990+
/// @param conformance if non-null, will check only for errors specific to the
991+
/// provided conformance. Otherwise, checks for _any_ errors.
992+
///
993+
/// @returns true iff there are any delayed diagnostic errors
994+
bool hasDelayedConformanceErrors(
995+
NormalProtocolConformance const* conformance = nullptr) const;
990996

991997
/// Add a delayed diagnostic produced while type-checking a
992998
/// particular protocol conformance.
@@ -996,7 +1002,7 @@ class ASTContext final {
9961002
/// Retrieve the delayed-conformance diagnostic callbacks for the
9971003
/// given normal protocol conformance.
9981004
std::vector<DelayedConformanceDiag>
999-
takeDelayedConformanceDiags(NormalProtocolConformance *conformance);
1005+
takeDelayedConformanceDiags(NormalProtocolConformance const* conformance);
10001006

10011007
/// Add delayed missing witnesses for the given normal protocol conformance.
10021008
void addDelayedMissingWitnesses(

lib/AST/ASTContext.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2207,13 +2207,28 @@ LazyIterableDeclContextData *ASTContext::getOrCreateLazyIterableContextData(
22072207
lazyLoader);
22082208
}
22092209

2210-
bool ASTContext::hasDelayedConformanceErrors() const {
2211-
for (const auto &entry : getImpl().DelayedConformanceDiags) {
2212-
auto &diagnostics = entry.getSecond();
2213-
if (std::any_of(diagnostics.begin(), diagnostics.end(),
2214-
[](const ASTContext::DelayedConformanceDiag &diag) {
2210+
bool ASTContext::hasDelayedConformanceErrors(
2211+
NormalProtocolConformance const* conformance) const {
2212+
2213+
auto hasDelayedErrors = [](std::vector<DelayedConformanceDiag> const& diags) {
2214+
return std::any_of(diags.begin(), diags.end(),
2215+
[](ASTContext::DelayedConformanceDiag const& diag) {
22152216
return diag.IsError;
2216-
}))
2217+
});
2218+
};
2219+
2220+
if (conformance) {
2221+
auto entry = getImpl().DelayedConformanceDiags.find(conformance);
2222+
if (entry != getImpl().DelayedConformanceDiags.end())
2223+
return hasDelayedErrors(entry->second);
2224+
2225+
return false; // unknown conformance, so no delayed delayed diags either.
2226+
}
2227+
2228+
// check all conformances for any delayed errors
2229+
for (const auto &entry : getImpl().DelayedConformanceDiags) {
2230+
auto const& diagnostics = entry.getSecond();
2231+
if (hasDelayedErrors(diagnostics))
22172232
return true;
22182233
}
22192234

@@ -2247,9 +2262,9 @@ ASTContext::takeDelayedMissingWitnesses(
22472262
}
22482263

22492264
std::vector<ASTContext::DelayedConformanceDiag>
2250-
ASTContext::takeDelayedConformanceDiags(NormalProtocolConformance *conformance){
2265+
ASTContext::takeDelayedConformanceDiags(NormalProtocolConformance const* cnfrm){
22512266
std::vector<ASTContext::DelayedConformanceDiag> result;
2252-
auto known = getImpl().DelayedConformanceDiags.find(conformance);
2267+
auto known = getImpl().DelayedConformanceDiags.find(cnfrm);
22532268
if (known != getImpl().DelayedConformanceDiags.end()) {
22542269
result = std::move(known->second);
22552270
getImpl().DelayedConformanceDiags.erase(known);

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,17 @@ class swift::MultiConformanceChecker {
15431543
public:
15441544
MultiConformanceChecker(ASTContext &ctx) : Context(ctx) {}
15451545

1546+
~MultiConformanceChecker() {
1547+
// force-flush diagnostics in checkers that have not already complained
1548+
for (auto &checker : AllUsedCheckers) {
1549+
if (checker.AlreadyComplained)
1550+
continue;
1551+
1552+
checker.SuppressDiagnostics = false; // no need to restore to prev value
1553+
checker.emitDelayedDiags();
1554+
}
1555+
}
1556+
15461557
ASTContext &getASTContext() const { return Context; }
15471558

15481559
/// Add a conformance into the batched checker.
@@ -2546,6 +2557,13 @@ ConformanceChecker::ConformanceChecker(
25462557
LocalMissingWitnessesStartIndex(GlobalMissingWitnesses.size()),
25472558
SuppressDiagnostics(suppressDiagnostics) {}
25482559

2560+
ConformanceChecker::~ConformanceChecker() {
2561+
// its not OK to forget about error diagnostics, unless if we have already
2562+
// complained or are suppose to suppress diagnostics.
2563+
assert(AlreadyComplained || SuppressDiagnostics ||
2564+
!getASTContext().hasDelayedConformanceErrors(Conformance));
2565+
}
2566+
25492567
ArrayRef<AssociatedTypeDecl *>
25502568
ConformanceChecker::getReferencedAssociatedTypes(ValueDecl *req) {
25512569
// Check whether we've already cached this information.

lib/Sema/TypeCheckProtocol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,8 @@ class ConformanceChecker : public WitnessChecker {
815815
llvm::SetVector<MissingWitness> &GlobalMissingWitnesses,
816816
bool suppressDiagnostics = true);
817817

818+
~ConformanceChecker();
819+
818820
/// Resolve all of the type witnesses.
819821
void resolveTypeWitnesses();
820822

0 commit comments

Comments
 (0)