Skip to content

Commit 24814c5

Browse files
authored
Merge pull request #71041 from slavapestov/refactor-conformance-checker-diagnostics
Refactor conformance checker diagnostics
2 parents 099045a + 0bc2e3f commit 24814c5

27 files changed

+324
-299
lines changed

include/swift/AST/ASTContext.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,9 +1313,8 @@ class ASTContext final {
13131313
/// conformance itself, along with a bit indicating whether this diagnostic
13141314
/// produces an error.
13151315
struct DelayedConformanceDiag {
1316-
const ValueDecl *Requirement;
1317-
std::function<void()> Callback;
13181316
bool IsError;
1317+
std::function<void(NormalProtocolConformance *)> Callback;
13191318
};
13201319

13211320
/// Check whether current context has any errors associated with
@@ -1330,8 +1329,9 @@ class ASTContext final {
13301329

13311330
/// Add a delayed diagnostic produced while type-checking a
13321331
/// particular protocol conformance.
1333-
void addDelayedConformanceDiag(NormalProtocolConformance *conformance,
1334-
DelayedConformanceDiag fn);
1332+
void addDelayedConformanceDiag(
1333+
NormalProtocolConformance *conformance, bool isError,
1334+
std::function<void(NormalProtocolConformance *)> callback);
13351335

13361336
/// Retrieve the delayed-conformance diagnostic callbacks for the
13371337
/// given normal protocol conformance.

include/swift/AST/AnyRequest.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ struct AnyRequestVTable {
5858
static void noteCycleStep(const void *ptr, DiagnosticEngine &diags) {
5959
static_cast<const Request *>(ptr)->noteCycleStep(diags);
6060
}
61+
static SourceLoc getNearestLoc(const void *ptr) {
62+
return static_cast<const Request *>(ptr)->getNearestLoc();
63+
}
6164
};
6265

6366
const uint64_t typeID;
@@ -66,6 +69,7 @@ struct AnyRequestVTable {
6669
const std::function<void(const void *, llvm::raw_ostream &)> simpleDisplay;
6770
const std::function<void(const void *, DiagnosticEngine &)> diagnoseCycle;
6871
const std::function<void(const void *, DiagnosticEngine &)> noteCycleStep;
72+
const std::function<SourceLoc(const void *)> getNearestLoc;
6973

7074
template <typename Request>
7175
static const AnyRequestVTable *get() {
@@ -75,7 +79,8 @@ struct AnyRequestVTable {
7579
&Impl<Request>::isEqual,
7680
&Impl<Request>::simpleDisplay,
7781
&Impl<Request>::diagnoseCycle,
78-
&Impl<Request>::noteCycleStep
82+
&Impl<Request>::noteCycleStep,
83+
&Impl<Request>::getNearestLoc
7984
};
8085
return &vtable;
8186
}
@@ -173,6 +178,11 @@ class AnyRequestBase {
173178
getVTable()->noteCycleStep(getRawStorage(), diags);
174179
}
175180

181+
/// Get the best source location describing the parameters to this request.
182+
SourceLoc getNearestLoc() const {
183+
return getVTable()->getNearestLoc(getRawStorage());
184+
}
185+
176186
/// Compare two instances for equality.
177187
friend bool operator==(const AnyRequestBase<Derived> &lhs,
178188
const AnyRequestBase<Derived> &rhs) {
@@ -224,6 +234,8 @@ class AnyRequestBase {
224234
/// - Cycle diagnostics operations:
225235
/// void diagnoseCycle(DiagnosticEngine &diags) const;
226236
/// void noteCycleStep(DiagnosticEngine &diags) const;
237+
/// - Source location information:
238+
/// SourceLoc getNearestLoc() const;
227239
///
228240
class ActiveRequest final : public AnyRequestBase<ActiveRequest> {
229241
template <typename T>

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5342,7 +5342,7 @@ ERROR(actor_protocol_illegal_inheritance,none,
53425342
"non-actor type %0 cannot conform to the %1 protocol",
53435343
(DeclName, DeclName))
53445344
ERROR(distributed_actor_conformance_missing_system_type,none,
5345-
"distributed actor %0 does not declare ActorSystem it can be used with.",
5345+
"distributed actor %0 does not declare ActorSystem it can be used with",
53465346
(DeclName))
53475347
NOTE(note_distributed_actor_system_can_be_defined_using_defaultdistributedactorsystem,none,
53485348
"you can provide a module-wide default actor system by declaring:\n"

include/swift/AST/Evaluator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ class Evaluator {
229229
/// diagnostics through the given diagnostics engine.
230230
Evaluator(DiagnosticEngine &diags, const LangOptions &opts);
231231

232+
/// For last-ditch diagnostics, get a good approximate source location for
233+
/// the thing we're currently type checking by searching for a request whose
234+
/// source location matches the predicate.
235+
SourceLoc getInnermostSourceLoc(llvm::function_ref<bool(SourceLoc)> fn);
236+
232237
/// Emit GraphViz output visualizing the request graph.
233238
void emitRequestEvaluatorGraphViz(llvm::StringRef graphVizPath);
234239

include/swift/AST/Expr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6403,6 +6403,8 @@ void simple_display(llvm::raw_ostream &out, const DefaultArgumentExpr *expr);
64036403
void simple_display(llvm::raw_ostream &out, const Expr *expr);
64046404

64056405
SourceLoc extractNearestSourceLoc(const DefaultArgumentExpr *expr);
6406+
SourceLoc extractNearestSourceLoc(const MacroExpansionExpr *expr);
6407+
SourceLoc extractNearestSourceLoc(const ClosureExpr *expr);
64066408

64076409
} // end namespace swift
64086410

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,6 @@ class ProtocolConformanceRef {
213213
/// Create a canonical conformance from the current one.
214214
ProtocolConformanceRef getCanonicalConformanceRef() const;
215215

216-
/// Get any additional requirements that are required for this conformance to
217-
/// be satisfied, if they're possible to compute.
218-
llvm::Optional<ArrayRef<Requirement>>
219-
getConditionalRequirementsIfAvailable() const;
220-
221216
/// Get any additional requirements that are required for this conformance to
222217
/// be satisfied.
223218
ArrayRef<Requirement> getConditionalRequirements() const;

lib/AST/ASTContext.cpp

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,28 @@ template <> struct DenseMapInfo<OverrideSignatureKey> {
192192
};
193193
} // namespace llvm
194194

195+
namespace {
196+
197+
/// If the conformance is in a primary file, we might diagnose some failures
198+
/// early via request evaluation, with all remaining failures diagnosed when
199+
/// we completely force the conformance from typeCheckDecl(). To emit the
200+
/// diagnostics together, we batch them up in the Diags vector.
201+
///
202+
/// If the conformance is in a secondary file, we instead just diagnose a
203+
/// generic "T does not conform to P" error the first time we hit an error
204+
/// via request evaluation. The detailed delayed conformance diagnostics
205+
/// are discarded, since we'll emit them again when we compile the file as
206+
/// a primary file.
207+
struct DelayedConformanceDiags {
208+
/// We set this if we've ever seen an error diagnostic here.
209+
bool HadError = false;
210+
211+
/// The delayed conformance diagnostics that have not been emitted yet.
212+
/// Never actually emitted for a secondary file.
213+
std::vector<ASTContext::DelayedConformanceDiag> Diags;
214+
};
215+
216+
}
195217
struct ASTContext::Implementation {
196218
Implementation();
197219
~Implementation();
@@ -354,8 +376,7 @@ struct ASTContext::Implementation {
354376

355377
/// Map from normal protocol conformances to diagnostics that have
356378
/// been delayed until the conformance is fully checked.
357-
llvm::DenseMap<NormalProtocolConformance *,
358-
std::vector<ASTContext::DelayedConformanceDiag>>
379+
llvm::DenseMap<NormalProtocolConformance *, ::DelayedConformanceDiags>
359380
DelayedConformanceDiags;
360381

361382
/// Map from normal protocol conformances to missing witnesses that have
@@ -1865,7 +1886,7 @@ void ASTContext::addCleanup(std::function<void(void)> cleanup) {
18651886
}
18661887

18671888
bool ASTContext::hadError() const {
1868-
return Diags.hadAnyError();
1889+
return Diags.hadAnyError() || hasDelayedConformanceErrors();
18691890
}
18701891

18711892
/// Retrieve the arena from which we should allocate storage for a type.
@@ -2735,25 +2756,18 @@ LazyIterableDeclContextData *ASTContext::getOrCreateLazyIterableContextData(
27352756
bool ASTContext::hasDelayedConformanceErrors(
27362757
NormalProtocolConformance const* conformance) const {
27372758

2738-
auto hasDelayedErrors = [](std::vector<DelayedConformanceDiag> const& diags) {
2739-
return std::any_of(diags.begin(), diags.end(),
2740-
[](ASTContext::DelayedConformanceDiag const& diag) {
2741-
return diag.IsError;
2742-
});
2743-
};
2744-
27452759
if (conformance) {
27462760
auto entry = getImpl().DelayedConformanceDiags.find(conformance);
27472761
if (entry != getImpl().DelayedConformanceDiags.end())
2748-
return hasDelayedErrors(entry->second);
2762+
return entry->second.HadError;
27492763

27502764
return false; // unknown conformance, so no delayed diags either.
27512765
}
27522766

27532767
// check all conformances for any delayed errors
27542768
for (const auto &entry : getImpl().DelayedConformanceDiags) {
27552769
auto const& diagnostics = entry.getSecond();
2756-
if (hasDelayedErrors(diagnostics))
2770+
if (diagnostics.HadError)
27572771
return true;
27582772
}
27592773

@@ -2763,9 +2777,49 @@ bool ASTContext::hasDelayedConformanceErrors(
27632777
MissingWitnessesBase::~MissingWitnessesBase() { }
27642778

27652779
void ASTContext::addDelayedConformanceDiag(
2766-
NormalProtocolConformance *conformance,
2767-
DelayedConformanceDiag fn) {
2768-
getImpl().DelayedConformanceDiags[conformance].push_back(std::move(fn));
2780+
NormalProtocolConformance *conformance, bool isError,
2781+
std::function<void(NormalProtocolConformance *)> callback) {
2782+
if (isError)
2783+
conformance->setInvalid();
2784+
2785+
auto &diagnostics = getImpl().DelayedConformanceDiags[conformance];
2786+
2787+
if (isError && !diagnostics.HadError) {
2788+
diagnostics.HadError = true;
2789+
2790+
auto *proto = conformance->getProtocol();
2791+
auto *dc = conformance->getDeclContext();
2792+
auto *sf = dc->getParentSourceFile();
2793+
auto *mod = sf->getParentModule();
2794+
assert(mod->isMainModule());
2795+
2796+
// If we have at least one primary file and the conformance is declared in a
2797+
// non-primary file, emit a fallback diagnostic.
2798+
if ((!sf->isPrimary() && !mod->getPrimarySourceFiles().empty()) ||
2799+
TypeCheckerOpts.EnableLazyTypecheck) {
2800+
auto complainLoc = evaluator.getInnermostSourceLoc([&](SourceLoc loc) {
2801+
if (loc.isInvalid())
2802+
return false;
2803+
2804+
auto *otherSF = mod->getSourceFileContainingLocation(loc);
2805+
if (otherSF == nullptr)
2806+
return false;
2807+
2808+
return otherSF->isPrimary();
2809+
});
2810+
2811+
if (complainLoc.isInvalid()) {
2812+
complainLoc = conformance->getLoc();
2813+
}
2814+
2815+
Diags.diagnose(complainLoc,
2816+
diag::type_does_not_conform,
2817+
dc->getSelfInterfaceType(),
2818+
proto->getDeclaredInterfaceType());
2819+
}
2820+
}
2821+
2822+
diagnostics.Diags.push_back({isError, callback});
27692823
}
27702824

27712825
void ASTContext::addDelayedMissingWitnesses(
@@ -2791,8 +2845,7 @@ ASTContext::takeDelayedConformanceDiags(NormalProtocolConformance const* cnfrm){
27912845
std::vector<ASTContext::DelayedConformanceDiag> result;
27922846
auto known = getImpl().DelayedConformanceDiags.find(cnfrm);
27932847
if (known != getImpl().DelayedConformanceDiags.end()) {
2794-
result = std::move(known->second);
2795-
getImpl().DelayedConformanceDiags.erase(known);
2848+
std::swap(result, known->second.Diags);
27962849
}
27972850
return result;
27982851
}

lib/AST/Evaluator.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ Evaluator::Evaluator(DiagnosticEngine &diags, const LangOptions &opts)
6060
debugDumpCycles(opts.DebugDumpCycles),
6161
recorder(opts.RecordRequestReferences) {}
6262

63+
SourceLoc Evaluator::getInnermostSourceLoc(
64+
llvm::function_ref<bool(SourceLoc)> fn) {
65+
for (auto request : llvm::reverse(activeRequests)) {
66+
SourceLoc loc = request.getNearestLoc();
67+
if (fn(loc))
68+
return loc;
69+
}
70+
71+
return SourceLoc();
72+
}
73+
6374
bool Evaluator::checkDependency(const ActiveRequest &request) {
6475
// Record this as an active request.
6576
if (activeRequests.insert(request)) {

lib/AST/Expr.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2835,10 +2835,18 @@ void swift::simple_display(llvm::raw_ostream &out,
28352835
out << "expression";
28362836
}
28372837

2838+
SourceLoc swift::extractNearestSourceLoc(const ClosureExpr *expr) {
2839+
return expr->getLoc();
2840+
}
2841+
28382842
SourceLoc swift::extractNearestSourceLoc(const DefaultArgumentExpr *expr) {
28392843
return expr->getLoc();
28402844
}
28412845

2846+
SourceLoc swift::extractNearestSourceLoc(const MacroExpansionExpr *expr) {
2847+
return expr->getLoc();
2848+
}
2849+
28422850
// See swift/Basic/Statistic.h for declaration: this enables tracing Exprs, is
28432851
// defined here to avoid too much layering violation / circular linkage
28442852
// dependency.

lib/AST/ProtocolConformance.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -483,12 +483,6 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
483483
return { Type(), nullptr };
484484
}
485485

486-
// If the conditional requirements aren't known, we can't properly run
487-
// inference.
488-
if (!getConditionalRequirementsIfAvailable()) {
489-
return TypeWitnessAndDecl();
490-
}
491-
492486
return evaluateOrDefault(
493487
assocType->getASTContext().evaluator,
494488
TypeWitnessRequest{const_cast<NormalProtocolConformance *>(this),

lib/AST/ProtocolConformanceRef.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -189,17 +189,6 @@ ProtocolConformanceRef::getWitnessByName(Type type, DeclName name) const {
189189
return getConcrete()->getWitnessDeclRef(requirement);
190190
}
191191

192-
llvm::Optional<ArrayRef<Requirement>>
193-
ProtocolConformanceRef::getConditionalRequirementsIfAvailable() const {
194-
if (isConcrete())
195-
return getConcrete()->getConditionalRequirementsIfAvailable();
196-
else
197-
// An abstract conformance is never conditional: any conditionality in the
198-
// concrete types that will eventually pass through this at runtime is
199-
// completely pre-checked and packaged up.
200-
return ArrayRef<Requirement>();
201-
}
202-
203192
ArrayRef<Requirement>
204193
ProtocolConformanceRef::getConditionalRequirements() const {
205194
if (isConcrete())

lib/Sema/ConstraintSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7235,7 +7235,7 @@ void ConstraintSystem::maybeProduceFallbackDiagnostic(
72357235
// diagnostics already emitted or waiting to be emitted. Because they are
72367236
// a better indication of the problem.
72377237
ASTContext &ctx = getASTContext();
7238-
if (ctx.Diags.hadAnyError() || ctx.hasDelayedConformanceErrors())
7238+
if (ctx.hadError())
72397239
return;
72407240

72417241
ctx.Diags.diagnose(target.getLoc(), diag::failed_to_produce_diagnostic);

0 commit comments

Comments
 (0)