Skip to content

Commit 0b0ad73

Browse files
committed
AST: Record a HadError bit in DelayedConformanceDiags
1 parent 4205310 commit 0b0ad73

File tree

8 files changed

+105
-30
lines changed

8 files changed

+105
-30
lines changed

lib/AST/ASTContext.cpp

Lines changed: 64 additions & 14 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
@@ -2733,25 +2754,18 @@ LazyIterableDeclContextData *ASTContext::getOrCreateLazyIterableContextData(
27332754
bool ASTContext::hasDelayedConformanceErrors(
27342755
NormalProtocolConformance const* conformance) const {
27352756

2736-
auto hasDelayedErrors = [](std::vector<DelayedConformanceDiag> const& diags) {
2737-
return std::any_of(diags.begin(), diags.end(),
2738-
[](ASTContext::DelayedConformanceDiag const& diag) {
2739-
return diag.IsError;
2740-
});
2741-
};
2742-
27432757
if (conformance) {
27442758
auto entry = getImpl().DelayedConformanceDiags.find(conformance);
27452759
if (entry != getImpl().DelayedConformanceDiags.end())
2746-
return hasDelayedErrors(entry->second);
2760+
return entry->second.HadError;
27472761

27482762
return false; // unknown conformance, so no delayed diags either.
27492763
}
27502764

27512765
// check all conformances for any delayed errors
27522766
for (const auto &entry : getImpl().DelayedConformanceDiags) {
27532767
auto const& diagnostics = entry.getSecond();
2754-
if (hasDelayedErrors(diagnostics))
2768+
if (diagnostics.HadError)
27552769
return true;
27562770
}
27572771

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

27692820
void ASTContext::addDelayedMissingWitnesses(
@@ -2789,8 +2840,7 @@ ASTContext::takeDelayedConformanceDiags(NormalProtocolConformance const* cnfrm){
27892840
std::vector<ASTContext::DelayedConformanceDiag> result;
27902841
auto known = getImpl().DelayedConformanceDiags.find(cnfrm);
27912842
if (known != getImpl().DelayedConformanceDiags.end()) {
2792-
result = std::move(known->second);
2793-
getImpl().DelayedConformanceDiags.erase(known);
2843+
std::swap(result, known->second.Diags);
27942844
}
27952845
return result;
27962846
}

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2916,12 +2916,7 @@ ConformanceChecker::ConformanceChecker(
29162916
LocalMissingWitnessesStartIndex(GlobalMissingWitnesses.size()),
29172917
SuppressDiagnostics(suppressDiagnostics) {}
29182918

2919-
ConformanceChecker::~ConformanceChecker() {
2920-
// its not OK to forget about error diagnostics, unless if we have already
2921-
// complained or are suppose to suppress diagnostics.
2922-
assert(AlreadyComplained || SuppressDiagnostics ||
2923-
!getASTContext().hasDelayedConformanceErrors(Conformance));
2924-
}
2919+
ConformanceChecker::~ConformanceChecker() {}
29252920

29262921

29272922
TinyPtrVector<AssociatedTypeDecl *>

test/SILGen/Inputs/dynamic_other.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class FromOtherFile: Proto {
1515
@objc init(objc: Int) {}
1616
@objc func objcMethod() {}
1717
@objc var objcProp: Int = 0
18-
@objc subscript(objc objc: Int) -> Int {
19-
get { return objc }
18+
@objc subscript(objc objc: AnyObject) -> Int {
19+
get { return 0 }
2020
set {}
2121
}
2222

@@ -29,6 +29,11 @@ class FromOtherFile: Proto {
2929
set {}
3030
}
3131

32+
static subscript(nativeType nativeType: Int) -> Int {
33+
get { return nativeType }
34+
set {}
35+
}
36+
3237
func overriddenByDynamic() {}
3338

3439
@NSManaged var managedProp: Int

test/SILGen/dynamic.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,9 @@ func objcMethodDispatchFromOtherFile() {
409409
// CHECK: class_method {{%.*}} : $FromOtherFile, #FromOtherFile.objcProp!setter :
410410
c.objcProp = x
411411
// CHECK: class_method {{%.*}} : $FromOtherFile, #FromOtherFile.subscript!getter :
412-
let y = c[objc: 0]
412+
let y = c[objc: 0 as AnyObject]
413413
// CHECK: class_method {{%.*}} : $FromOtherFile, #FromOtherFile.subscript!setter :
414-
c[objc: 0] = y
414+
c[objc: 0 as AnyObject] = y
415415
}
416416

417417
// CHECK-LABEL: sil hidden [ossa] @$s7dynamic0A27MethodDispatchFromOtherFileyyF : $@convention(thin) () -> ()
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -typecheck -experimental-lazy-typecheck -emit-tbd -emit-tbd-path %t/lazy.tbd %s -enable-library-evolution -parse-as-library -tbd-install_name lazy
2+
// RUN: %target-swift-frontend -typecheck -verify -experimental-lazy-typecheck -emit-tbd -emit-tbd-path %t/lazy.tbd %s -enable-library-evolution -parse-as-library -tbd-install_name lazy
33

44
public protocol P {
55
func req()
66
}
77

8-
// FIXME: This malformed conformance should probably be diagnosed.
8+
// expected-error@+1 {{type 'S' does not conform to protocol 'P'}}
99
public struct S: P {
1010
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-frontend -typecheck -verify -primary-file %t/Usage.swift %t/Conformance.swift
4+
5+
//--- Conformance.swift
6+
protocol P {
7+
associatedtype A: Equatable
8+
func f() -> A
9+
}
10+
11+
extension P {
12+
func f() -> A { fatalError() }
13+
}
14+
15+
struct S: P {}
16+
17+
//--- Usage.swift
18+
func callee(_: some Equatable) {}
19+
20+
// FIXME: This source location will become more accurate once TypeCheckExpr
21+
// is a request! For now, pointing at the function is still better than crashing.
22+
23+
func caller() { // expected-error {{type 'S' does not conform to protocol 'P'}}
24+
callee(S().f())
25+
}

test/multifile/protocol-conformance-issue-53408.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
func reproducer() -> Float { return Struct().func1(1.0) }
77
// expected-error@-1 {{cannot convert value of type 'Double' to expected argument type 'Struct.Input'}}
8+
// expected-error@-2 {{type 'Struct' does not conform to protocol 'Proto1'}}

validation-test/compiler_crashers_2_fixed/0127-issue-48118.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: not %target-swift-frontend %s -typecheck
2-
// REQUIRES: asserts
1+
// RUN: %target-typecheck-verify-swift
32

43
// https://github.com/apple/swift/issues/48118
54

@@ -18,7 +17,7 @@ public protocol P {
1817
func f4(_ x: Context)
1918
}
2019

21-
public extension P {
20+
extension P {
2221
public func f1(_ x: Context, _ y: PA) {
2322
}
2423
public func f2(_ x: Context, _ y: PB) {
@@ -35,6 +34,6 @@ public struct S: P {
3534

3635
public func f1(_ x: Context, _ y: PA) {
3736
}
38-
public func f2(_ x: Context, _ y: PB) {
37+
public func f2(_ x: Context, _ y: PB) { // expected-error {{reference to invalid type alias 'Context' of type 'S'}}
3938
}
4039
}

0 commit comments

Comments
 (0)