@@ -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+ }
195217struct 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
18671888bool 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(
27352756bool 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(
27632777MissingWitnessesBase::~MissingWitnessesBase () { }
27642778
27652779void 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
27712825void 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}
0 commit comments