Skip to content

Commit d4522fa

Browse files
committed
[Sema] emit conditional Copyable conformance note
For containment issues, where an unconditionally or conditionally Copyable type contains a fixed noncopyable element, remind users about the conditional conformance in addition to the fix-it to add ~Copyable
1 parent f8d688c commit d4522fa

File tree

2 files changed

+45
-29
lines changed

2 files changed

+45
-29
lines changed

lib/Sema/TypeCheckInvertible.cpp

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,29 @@ static void tryEmitContainmentFixits(NominalTypeDecl *enclosingNom,
5858
auto *module = enclosingNom->getParentModule();
5959
auto &ctx = enclosingNom->getASTContext();
6060

61-
// First and most universal suggestion, add the inverse to the enclosing type.
62-
{
61+
// Check the enclosing type's markings to see what to suggest.
62+
assert(kp == KnownProtocolKind::Copyable);
63+
auto enclosingMarking = enclosingNom->getNoncopyableMarking();
64+
65+
switch (enclosingMarking.getInverse().getKind()) {
66+
case InverseMarking::Kind::Inferred:
67+
// Note that the enclosing type is conditionally conforming to KP first.
68+
ctx.Diags.diagnose(enclosingMarking.getInverse().getLoc(),
69+
diag::note_inverse_preventing_conformance_implicit,
70+
enclosingNom, getProtocolName(kp));
71+
LLVM_FALLTHROUGH;
72+
case InverseMarking::Kind::None: {
73+
// Suggest adding ~KP to make it non-KP.
6374
auto diag = enclosingNom->diagnose(diag::add_inverse,
6475
enclosingNom,
6576
getProtocolName(kp));
6677
addConformanceFixIt(enclosingNom, diag, kp, /*inverse=*/true);
6778
}
79+
break;
80+
case InverseMarking::Kind::Explicit:
81+
assert(false && "how did it become Copyable?");
82+
break;
83+
};
6884

6985
// If it's a generic parameter defined in the same module, point to the
7086
// parameter that must have had the inverse applied to it somewhere.
@@ -83,31 +99,29 @@ static void tryEmitContainmentFixits(NominalTypeDecl *enclosingNom,
8399
return;
84100
}
85101

86-
if (kp == KnownProtocolKind::Copyable) {
87-
// If the offending type is a nominal with a SourceLoc, explain why it's
88-
// not Copyable.
89-
if (auto nominal = nonConformingTy->getAnyNominal()) {
90-
if (nominal->getLoc(/*SerializedOK=*/false)) {
91-
auto inverse = nominal->getNoncopyableMarking().getInverse();
92-
auto loc = inverse.getLoc();
93-
94-
switch (inverse.getKind()) {
95-
case InverseMarking::Kind::None:
96-
assert(false && "how did it become noncopyable then?");
97-
break;
98-
case InverseMarking::Kind::Inferred:
99-
assert(loc);
100-
ctx.Diags.diagnose(loc,
101-
diag::note_inverse_preventing_conformance_implicit,
102-
nominal, getProtocolName(kp));
103-
break;
104-
case InverseMarking::Kind::Explicit:
105-
assert(loc);
106-
ctx.Diags.diagnose(loc,
107-
diag::note_inverse_preventing_conformance_explicit,
108-
nominal, getProtocolName(kp));
109-
break;
110-
}
102+
// If the offending type is a nominal with a SourceLoc, explain why it's
103+
// not Copyable.
104+
if (auto nominal = nonConformingTy->getAnyNominal()) {
105+
if (nominal->getLoc(/*SerializedOK=*/false)) {
106+
auto inverse = nominal->getNoncopyableMarking().getInverse();
107+
auto loc = inverse.getLoc();
108+
109+
switch (inverse.getKind()) {
110+
case InverseMarking::Kind::None:
111+
assert(false && "how did it become noncopyable then?");
112+
break;
113+
case InverseMarking::Kind::Inferred:
114+
assert(loc);
115+
ctx.Diags.diagnose(loc,
116+
diag::note_inverse_preventing_conformance_implicit,
117+
nominal, getProtocolName(kp));
118+
break;
119+
case InverseMarking::Kind::Explicit:
120+
assert(loc);
121+
ctx.Diags.diagnose(loc,
122+
diag::note_inverse_preventing_conformance_explicit,
123+
nominal, getProtocolName(kp));
124+
break;
111125
}
112126
}
113127
}
@@ -322,8 +336,6 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
322336
return generateConformance(ext);
323337
};
324338

325-
DeclContext *conformanceDC = nominal;
326-
327339
switch (*ip) {
328340
case InvertibleProtocolKind::Copyable: {
329341
auto marking = nominal->getNoncopyableMarking();

test/Generics/inverse_copyable_generics.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ protocol RemovedAgain where Self: ~Copyable {
4747

4848
struct StructContainment<T: ~Copyable> : Copyable {
4949
// expected-note@-1 {{consider adding '~Copyable' to generic struct 'StructContainment'}}{{50-50=, ~Copyable}}
50+
// expected-note@-2 {{generic struct 'StructContainment' has '~Copyable' constraint on a generic parameter, making its 'Copyable' conformance conditional}}
5051

5152
var storage: Maybe<T>
5253
// expected-error@-1 {{stored property 'storage' of 'Copyable'-conforming generic struct 'StructContainment' has noncopyable type 'Maybe<T>'}}
@@ -55,6 +56,7 @@ struct StructContainment<T: ~Copyable> : Copyable {
5556
enum EnumContainment<T: ~Copyable> : Copyable {
5657
// expected-note@-1 {{'T' has '~Copyable' constraint preventing implicit 'Copyable' conformance}}
5758
// expected-note@-2{{consider adding '~Copyable' to generic enum 'EnumContainment'}}{{46-46=, ~Copyable}}
59+
// expected-note@-3{{generic enum 'EnumContainment' has '~Copyable' constraint on a generic parameter, making its 'Copyable' conformance conditional}}
5860

5961
case some(T) // expected-error {{associated value 'some' of 'Copyable'-conforming generic enum 'EnumContainment' has noncopyable type 'T'}}
6062
case other(Int)
@@ -69,6 +71,7 @@ class ClassContainment<T: ~Copyable> {
6971
}
7072
}
7173

74+
// expected-note@+2 {{generic struct 'ConditionalContainment' has '~Copyable' constraint on a generic parameter, making its 'Copyable' conformance conditional}}
7275
// expected-note@+1 {{consider adding '~Copyable' to generic struct 'ConditionalContainment'}}{{45-45=: ~Copyable}}
7376
struct ConditionalContainment<T: ~Copyable> {
7477
var x: T
@@ -116,6 +119,7 @@ extension Maybe where Self: Copyable {
116119
func check2(_ t: RequireCopyable<Self>) {}
117120
}
118121

122+
// expected-note@+2 {{generic struct 'CornerCase' has '~Copyable' constraint on a generic parameter, making its 'Copyable' conformance conditional}}
119123
// expected-note@+1 {{consider adding '~Copyable' to generic struct 'CornerCase'}}{{33-33=: ~Copyable}}
120124
struct CornerCase<T: ~Copyable> {
121125
let t: T

0 commit comments

Comments
 (0)