Skip to content

Commit 6c9fb0d

Browse files
committed
Undo Bogus DiagnosticTransactions in Codable Synthesis
The order of diagnostic emission absolutely does not matter. What this transaction was actually doing was suppressing valid diagnostics. This is a deeply unsound thing to do since if errors are emitted but Codable synthesis succeeds then invalid code can make its way past Sema. rdar://74392492
1 parent fa8f030 commit 6c9fb0d

File tree

5 files changed

+31
-59
lines changed

5 files changed

+31
-59
lines changed

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,36 +1058,19 @@ ValueDecl *DerivedConformance::deriveEncodable(ValueDecl *requirement) {
10581058
if (checkAndDiagnoseDisallowedContext(requirement))
10591059
return nullptr;
10601060

1061-
// We're about to try to synthesize Encodable. If something goes wrong,
1062-
// we'll have to output at least one error diagnostic because we returned
1063-
// true from NominalTypeDecl::derivesProtocolConformance; if we don't, we're
1064-
// expected to return a witness here later (and we crash on an assertion).
1065-
// Producing a diagnostic stops compilation before then.
1066-
//
1067-
// A synthesis attempt will produce NOTE diagnostics throughout, but we'll
1068-
// want to collect them before displaying -- we want NOTEs to display
1069-
// _after_ a main diagnostic so we don't get a NOTE before the error it
1070-
// relates to.
1071-
//
1072-
// We can do this with a diagnostic transaction -- first collect failure
1073-
// diagnostics, then potentially collect notes. If we succeed in
1074-
// synthesizing Encodable, we can cancel the transaction and get rid of the
1075-
// fake failures.
1076-
DiagnosticTransaction diagnosticTransaction(Context.Diags);
1077-
ConformanceDecl->diagnose(diag::type_does_not_conform,
1078-
Nominal->getDeclaredType(), getProtocolType());
1079-
requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Func,
1080-
requirement->getName(), getProtocolType(),
1081-
/*AddFixIt=*/false);
1082-
10831061
// Check other preconditions for synthesized conformance.
10841062
// This synthesizes a CodingKeys enum if possible.
1085-
if (canSynthesize(*this, requirement)) {
1086-
diagnosticTransaction.abort();
1087-
return deriveEncodable_encode(*this);
1063+
if (!canSynthesize(*this, requirement)) {
1064+
ConformanceDecl->diagnose(diag::type_does_not_conform,
1065+
Nominal->getDeclaredType(), getProtocolType());
1066+
requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Func,
1067+
requirement->getName(), getProtocolType(),
1068+
/*AddFixIt=*/false);
1069+
1070+
return nullptr;
10881071
}
10891072

1090-
return nullptr;
1073+
return deriveEncodable_encode(*this);
10911074
}
10921075

10931076
ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) {
@@ -1104,24 +1087,17 @@ ValueDecl *DerivedConformance::deriveDecodable(ValueDecl *requirement) {
11041087
if (checkAndDiagnoseDisallowedContext(requirement))
11051088
return nullptr;
11061089

1107-
// We're about to try to synthesize Decodable. If something goes wrong,
1108-
// we'll have to output at least one error diagnostic. We need to collate
1109-
// diagnostics produced by canSynthesize and deriveDecodable_init to produce
1110-
// them in the right order -- see the comment in deriveEncodable for
1111-
// background on this transaction.
1112-
DiagnosticTransaction diagnosticTransaction(Context.Diags);
1113-
ConformanceDecl->diagnose(diag::type_does_not_conform,
1114-
Nominal->getDeclaredType(), getProtocolType());
1115-
requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Constructor,
1116-
requirement->getName(), getProtocolType(),
1117-
/*AddFixIt=*/false);
1118-
11191090
// Check other preconditions for synthesized conformance.
11201091
// This synthesizes a CodingKeys enum if possible.
1121-
if (canSynthesize(*this, requirement)) {
1122-
diagnosticTransaction.abort();
1123-
return deriveDecodable_init(*this);
1092+
if (!canSynthesize(*this, requirement)) {
1093+
ConformanceDecl->diagnose(diag::type_does_not_conform,
1094+
Nominal->getDeclaredType(), getProtocolType());
1095+
requirement->diagnose(diag::no_witnesses, diag::RequirementKind::Constructor,
1096+
requirement->getName(), getProtocolType(),
1097+
/*AddFixIt=*/false);
1098+
1099+
return nullptr;
11241100
}
11251101

1126-
return nullptr;
1102+
return deriveDecodable_init(*this);
11271103
}

test/decl/protocol/special/coding/class_codable_nonconforming_property.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,10 @@ class NonConformingClass : Codable { // expected-error {{type 'NonConformingClas
165165
// These lines have to be within the NonConformingClass type because
166166
// CodingKeys should be private.
167167
func foo() {
168-
// They should not get a CodingKeys type.
169-
let _ = NonConformingClass.CodingKeys.self // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
170-
let _ = NonConformingClass.CodingKeys.x // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
171-
let _ = NonConformingClass.CodingKeys.y // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
172-
let _ = NonConformingClass.CodingKeys.z // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
168+
let _ = NonConformingClass.CodingKeys.self
169+
let _ = NonConformingClass.CodingKeys.x
170+
let _ = NonConformingClass.CodingKeys.y
171+
let _ = NonConformingClass.CodingKeys.z // expected-error {{type 'NonConformingClass.CodingKeys' has no member 'z'}}
173172
}
174173
}
175174

@@ -178,4 +177,4 @@ let _ = NonConformingClass.init(from:) // expected-error {{type 'NonConformingCl
178177
let _ = NonConformingClass.encode(to:) // expected-error {{type 'NonConformingClass' has no member 'encode(to:)'}}
179178

180179
// They should not get a CodingKeys type.
181-
let _ = NonConformingClass.CodingKeys.self // expected-error {{type 'NonConformingClass' has no member 'CodingKeys'}}
180+
let _ = NonConformingClass.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}

test/decl/protocol/special/coding/struct_codable_nonconforming_property.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,10 @@ struct NonConformingStruct : Codable { // expected-error {{type 'NonConformingSt
165165
// These lines have to be within the NonConformingStruct type because
166166
// CodingKeys should be private.
167167
func foo() {
168-
// They should not get a CodingKeys type.
169-
let _ = NonConformingStruct.CodingKeys.self // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
170-
let _ = NonConformingStruct.CodingKeys.x // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
171-
let _ = NonConformingStruct.CodingKeys.y // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
172-
let _ = NonConformingStruct.CodingKeys.z // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
168+
let _ = NonConformingStruct.CodingKeys.self
169+
let _ = NonConformingStruct.CodingKeys.x
170+
let _ = NonConformingStruct.CodingKeys.y
171+
let _ = NonConformingStruct.CodingKeys.z // expected-error {{type 'NonConformingStruct.CodingKeys' has no member 'z'}}
173172
}
174173
}
175174

@@ -178,4 +177,4 @@ let _ = NonConformingStruct.init(from:) // expected-error {{type 'NonConformingS
178177
let _ = NonConformingStruct.encode(to:) // expected-error {{type 'NonConformingStruct' has no member 'encode(to:)'}}
179178

180179
// They should not get a CodingKeys type.
181-
let _ = NonConformingStruct.CodingKeys.self // expected-error {{type 'NonConformingStruct' has no member 'CodingKeys'}}
180+
let _ = NonConformingStruct.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}

test/decl/protocol/special/coding/struct_codable_simple.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ let _ = SimpleStruct.encode(to:)
3131
let _ = SimpleStruct.CodingKeys.self // expected-error {{'CodingKeys' is inaccessible due to 'private' protection level}}
3232

3333
// rdar://problem/59655704
34-
struct SR_12248_1: Codable { // expected-error {{type 'SR_12248_1' does not conform to protocol 'Encodable'}}
34+
struct SR_12248_1: Codable { // expected-error {{type 'SR_12248_1' does not conform to protocol 'Encodable'}} expected-error {{type 'SR_12248_1' does not conform to protocol 'Decodable'}}
3535
var x: Int // expected-note {{'x' previously declared here}}
3636
var x: Int // expected-error {{invalid redeclaration of 'x'}}
3737
// expected-note@-1 {{cannot automatically synthesize 'Encodable' because 'Int' does not conform to 'Encodable'}}
3838
// expected-note@-2 {{cannot automatically synthesize 'Encodable' because 'Int' does not conform to 'Encodable'}}
3939
}
4040

41-
struct SR_12248_2: Decodable {
41+
struct SR_12248_2: Decodable { // expected-error {{type 'SR_12248_2' does not conform to protocol 'Decodable'}}
4242
var x: Int // expected-note {{'x' previously declared here}}
4343
var x: Int // expected-error {{invalid redeclaration of 'x'}}
4444
}
4545

46-
struct SR_12248_3: Encodable {
46+
struct SR_12248_3: Encodable { // expected-error {{type 'SR_12248_3' does not conform to protocol 'Encodable'}}
4747
var x: Int // expected-note {{'x' previously declared here}}
4848
var x: Int // expected-error {{invalid redeclaration of 'x'}}
4949
}

test/diagnostics/pretty-printed-diagnostics.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,6 @@ foo(b:
136136
// CHECK: [[#LINE]] | struct B: Decodable {
137137
// CHECK: | ^ error: type 'B' does not conform to protocol 'Decodable'
138138
// CHECK: [[#LINE+1]] | let a: Foo
139-
// CHECK: | ^ note: cannot automatically synthesize 'Decodable' because 'Foo' does not conform to 'Decodable'
140-
// CHECK: [[#LINE+2]] | }
141139
// CHECK: Swift.Decodable:2:5
142140
// CHECK: 1 | public protocol Decodable {
143141
// CHECK: 2 | init(from decoder: Decoder) throws

0 commit comments

Comments
 (0)