@@ -189,16 +189,36 @@ bool checkCopyableConformance(ProtocolConformance *conformance) {
189
189
if (!nom)
190
190
return false ;
191
191
192
+ auto &ctx = nom->getASTContext ();
193
+ bool conforms = true ;
194
+
195
+ // An explicit `~Copyable` prevents conformance if any of these are true:
196
+ //
197
+ // 1. It appears on a class.
198
+ // 2. Appears on the same declaration that also declares the conformance.
199
+ // So, if the nominal has `~Copyable` but this conformance is
200
+ // written in an extension, then we do not raise an error.
201
+ auto marking = nom->getNoncopyableMarking ();
202
+ if (marking.getInverse ().getKind () == InverseMarking::Kind::Explicit) {
203
+ if (isa<ClassDecl>(nom)) {
204
+ ctx.Diags .diagnose (marking.getInverse ().getLoc (),
205
+ diag::noncopyable_class);
206
+ conforms &= false ;
207
+ } else if (conformance->getDeclContext () == nom) {
208
+ ctx.Diags .diagnose (marking.getInverse ().getLoc (),
209
+ diag::noncopyable_but_copyable,
210
+ nom);
211
+ conforms &= false ;
212
+ }
213
+ }
214
+
192
215
// All classes can store noncopyable values.
193
216
if (isa<ClassDecl>(nom))
194
- return true ;
217
+ return conforms ;
195
218
196
219
// Protocols do not directly define any storage.
197
- if (isa<ProtocolDecl>(nom))
198
- return true ;
199
-
200
- if (isa<BuiltinTupleDecl>(nom))
201
- llvm_unreachable (" TODO: BuiltinTupleDecl" );
220
+ if (isa<ProtocolDecl, BuiltinTupleDecl>(nom))
221
+ llvm_unreachable (" unexpected nominal to check Copyable conformance" );
202
222
203
223
// A deinit prevents a struct or enum from conforming to Copyable.
204
224
if (auto *deinit = nom->getValueTypeDestructor ()) {
@@ -207,7 +227,7 @@ bool checkCopyableConformance(ProtocolConformance *conformance) {
207
227
KnownProtocolKind::Copyable,
208
228
nom->getNoncopyableMarking (),
209
229
nom);
210
- return false ;
230
+ conforms &= false ;
211
231
}
212
232
213
233
@@ -256,8 +276,12 @@ bool checkCopyableConformance(ProtocolConformance *conformance) {
256
276
};
257
277
258
278
// This nominal cannot be Copyable if it contains noncopyable storage.
259
- return !HasNoncopyable (nom, conformance->getDeclContext (),
279
+ bool haveNoncopyableStorage =
280
+ HasNoncopyable (nom, conformance->getDeclContext (),
260
281
/* diagnose=*/ true ).visit ();
282
+ conforms &= !haveNoncopyableStorage;
283
+
284
+ return conforms;
261
285
}
262
286
263
287
// / Visit the instance storage of the given nominal type as seen through
@@ -372,23 +396,14 @@ ProtocolConformance *deriveConformanceForInvertible(Evaluator &evaluator,
372
396
373
397
switch (*ip) {
374
398
case InvertibleProtocolKind::Copyable: {
375
- auto marking = nominal->getNoncopyableMarking ();
376
-
377
- // An explicit Copyable takes precedece over any ~Copyable marking.
378
- if (marking.getPositive ().getKind () == InverseMarking::Kind::Explicit) {
379
- // If they also explicitly wrote ~Copyable, then diagnose that.
380
- auto inverse = marking.getInverse ();
381
- if (inverse.getKind () == InverseMarking::Kind::Explicit) {
382
- ctx.Diags .diagnose (inverse.getLoc (),
383
- diag::noncopyable_but_copyable,
384
- nominal);
385
- }
386
-
399
+ // Always derive unconditional Copyable conformance for classes
400
+ if (isa<ClassDecl>(nominal))
387
401
return generateConformance (nominal);
388
- }
389
402
390
- // Unexpected to have Kind::Inferred marking for Copyable; it's assumed.
391
- assert (marking.getPositive ().getKind () == InverseMarking::Kind::None);
403
+ auto marking = nominal->getNoncopyableMarking ();
404
+
405
+ // Unexpected to have any marking for Copyable if we're deriving it.
406
+ assert (!marking.getPositive ().isPresent ());
392
407
393
408
// Check what kind of inverse we have to determine whether to generate a
394
409
// conformance for Copyable.
0 commit comments