@@ -388,6 +388,11 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
388388 case SILInstructionKind::ExplicitCopyAddrInst:
389389 case SILInstructionKind::ExplicitCopyValueInst:
390390 break ; // Explicitly acknowledged copies are OK.
391+ case SILInstructionKind::CopyAddrInst: {
392+ if (!cast<CopyAddrInst>(inst)->isTakeOfSrc ())
393+ shouldDiagnose = true ; // If it isn't a [take], it's a copy.
394+ break ;
395+ }
391396 case SILInstructionKind::LoadInst: {
392397 // FIXME: we don't have an `explicit_load` and transparent functions can
393398 // end up bringing in a `load [copy]`. A better approach is needed to
@@ -429,38 +434,53 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
429434 << " \n has unexpected copying instruction: " << *inst);
430435
431436 // Try to come up with a useful diagnostic.
437+
438+ // First, identify what is being copied.
439+ SILValue copied;
432440 if (auto svi = dyn_cast<SingleValueInstruction>(inst)) {
433- if (auto name = VariableNameInferrer::inferName (svi)) {
434- // Simplistic check for whether this is a closure capture.
435- for (auto user : svi->getUsers ()) {
436- if (isa<PartialApplyInst>(user)) {
437- LLVM_DEBUG (llvm::dbgs () << " captured by " << *user);
438- diagnose (loc, diag::manualownership_copy_captured, *name);
439- return false ;
440- }
441- }
441+ copied = svi;
442+ } else if (auto cai = dyn_cast<CopyAddrInst>(inst)) {
443+ copied = cai->getSrc ();
444+ }
442445
443- // There's no hope of borrowing access if there's a consuming use.
444- for (auto op : svi->getUses ()) {
445- auto useKind = op->getOperandOwnership ();
446-
447- // Only some DestroyingConsume's, like 'store', are interesting.
448- if (useKind == OperandOwnership::ForwardingConsume
449- || isa<StoreInst>(op->getUser ())) {
450- LLVM_DEBUG (llvm::dbgs () << " demanded by " << *(op->getUser ()));
451- diagnose (loc, diag::manualownership_copy_demanded, *name);
452- return false ;
453- }
454- }
446+ // Find a name for that copied thing.
447+ std::optional<Identifier> name;
448+ if (copied)
449+ name = VariableNameInferrer::inferName (copied);
450+
451+ if (!name) {
452+ // Emit a rudimentary diagnostic.
453+ diagnose (loc, diag::manualownership_copy);
454+ return false ;
455+ }
455456
456- diagnose (loc, diag::manualownership_copy_happened, *name);
457+ // Try to tailor the diagnostic based on usages.
458+
459+ // Simplistic check for whether this is a closure capture.
460+ for (auto user : copied->getUsers ()) {
461+ if (isa<PartialApplyInst>(user)) {
462+ LLVM_DEBUG (llvm::dbgs () << " captured by " << *user);
463+ diagnose (loc, diag::manualownership_copy_captured, name->get ());
457464 return false ;
458465 }
459466 }
460467
461- // Back-up diagnostic, when all-else fails.
462- diagnose (loc, diag::manualownership_copy);
463- return false ; // Don't bail-out early; diagnose more issues in the func.
468+ // There's no hope of borrowing access if there's a consuming use.
469+ for (auto op : copied->getUses ()) {
470+ auto useKind = op->getOperandOwnership ();
471+
472+ // Only some DestroyingConsume's, like 'store', are interesting.
473+ if (useKind == OperandOwnership::ForwardingConsume
474+ || isa<StoreInst>(op->getUser ())) {
475+ LLVM_DEBUG (llvm::dbgs () << " demanded by " << *(op->getUser ()));
476+ diagnose (loc, diag::manualownership_copy_demanded, *name);
477+ return false ;
478+ }
479+ }
480+
481+ // Catch-all diagnostic for when we at least have the name.
482+ diagnose (loc, diag::manualownership_copy_happened, *name);
483+ return false ;
464484 }
465485 }
466486 return false ;
0 commit comments