@@ -416,10 +416,7 @@ void verifyKeyPathComponent(SILModule &M,
416
416
struct ImmutableAddressUseVerifier {
417
417
SmallVector<Operand *, 32 > worklist;
418
418
419
- bool isConsumingOrMutatingApplyUse (Operand *use) {
420
- ApplySite apply (use->getUser ());
421
- assert (apply && " Not an apply instruction kind" );
422
- auto conv = apply.getArgumentConvention (*use);
419
+ bool isConsumingOrMutatingArgumentConvention (SILArgumentConvention conv) {
423
420
switch (conv) {
424
421
case SILArgumentConvention::Indirect_In_Guaranteed:
425
422
return false ;
@@ -448,10 +445,24 @@ struct ImmutableAddressUseVerifier {
448
445
return true ; // return something "conservative".
449
446
}
450
447
llvm_unreachable (" covered switch isn't covered?!" );
451
- };
448
+ }
452
449
453
- // / A "copy_addr %src [take] to *" is consuming on "%src".
454
- // / A "copy_addr * to * %dst" is mutating on "%dst".
450
+ bool isConsumingOrMutatingApplyUse (Operand *use) {
451
+ ApplySite apply (use->getUser ());
452
+ assert (apply && " Not an apply instruction kind" );
453
+ auto conv = apply.getArgumentConvention (*use);
454
+ return isConsumingOrMutatingArgumentConvention (conv);
455
+ }
456
+
457
+ bool isConsumingOrMutatingYieldUse (Operand *use) {
458
+ // For now, just say that it is non-consuming for now.
459
+ auto *yield = cast<YieldInst>(use->getUser ());
460
+ auto conv = yield->getArgumentConventionForOperand (*use);
461
+ return isConsumingOrMutatingArgumentConvention (conv);
462
+ }
463
+
464
+ // A "copy_addr %src [take] to *" is consuming on "%src".
465
+ // A "copy_addr * to * %dst" is mutating on "%dst".
455
466
bool isConsumingOrMutatingCopyAddrUse (Operand *use) {
456
467
auto *copyAddr = cast<CopyAddrInst>(use->getUser ());
457
468
if (copyAddr->getDest () == use->get ())
@@ -462,37 +473,56 @@ struct ImmutableAddressUseVerifier {
462
473
}
463
474
464
475
bool isCastToNonConsuming (UncheckedAddrCastInst *i) {
465
- for (auto *use : i->getUses ()) {
476
+ // Check if any of our uses are consuming. If none of them are consuming, we
477
+ // are good to go.
478
+ return llvm::none_of (i->getUses (), [&](Operand *use) -> bool {
466
479
auto *inst = use->getUser ();
467
480
switch (inst->getKind ()) {
481
+ default :
482
+ return false ;
468
483
case SILInstructionKind::ApplyInst:
469
484
case SILInstructionKind::TryApplyInst:
470
485
case SILInstructionKind::PartialApplyInst:
471
- if (isConsumingOrMutatingApplyUse (use))
472
- return false ;
473
- continue ;
474
- default :
475
- continue ;
486
+ case SILInstructionKind::BeginApplyInst:
487
+ return isConsumingOrMutatingApplyUse (use);
476
488
}
477
- }
478
- return true ;
489
+ });
479
490
}
480
491
481
492
bool isMutatingOrConsuming (SILValue address) {
482
- for (auto *use : address->getUses ()) {
493
+ copy (address->getUses (), std::back_inserter (worklist));
494
+ while (!worklist.empty ()) {
495
+ auto *use = worklist.pop_back_val ();
483
496
auto *inst = use->getUser ();
484
497
if (inst->isTypeDependentOperand (*use))
485
498
continue ;
486
499
switch (inst->getKind ()) {
487
500
case SILInstructionKind::MarkDependenceInst:
501
+ case SILInstructionKind::LoadBorrowInst:
502
+ case SILInstructionKind::DebugValueAddrInst:
503
+ case SILInstructionKind::ExistentialMetatypeInst:
504
+ case SILInstructionKind::ValueMetatypeInst:
505
+ case SILInstructionKind::FixLifetimeInst:
506
+ case SILInstructionKind::KeyPathInst:
507
+ case SILInstructionKind::SwitchEnumAddrInst:
508
+ break ;
509
+ case SILInstructionKind::AddressToPointerInst:
510
+ // We assume that the user is attempting to do something unsafe since we
511
+ // are converting to a raw pointer. So just ignore this use.
512
+ //
513
+ // TODO: Can we do better?
488
514
break ;
489
515
case SILInstructionKind::ApplyInst:
490
516
case SILInstructionKind::TryApplyInst:
491
517
case SILInstructionKind::PartialApplyInst:
518
+ case SILInstructionKind::BeginApplyInst:
492
519
if (isConsumingOrMutatingApplyUse (use))
493
520
return true ;
494
- else
495
- break ;
521
+ break ;
522
+ case SILInstructionKind::YieldInst:
523
+ if (isConsumingOrMutatingYieldUse (use))
524
+ return true ;
525
+ break ;
496
526
case SILInstructionKind::CopyAddrInst:
497
527
if (isConsumingOrMutatingCopyAddrUse (use))
498
528
return true ;
@@ -519,11 +549,32 @@ struct ImmutableAddressUseVerifier {
519
549
break ;
520
550
case SILInstructionKind::LoadInst:
521
551
// A 'non-taking' value load is harmless.
522
- return cast<LoadInst>(inst)->getOwnershipQualifier () ==
523
- LoadOwnershipQualifier::Take;
552
+ if (cast<LoadInst>(inst)->getOwnershipQualifier () ==
553
+ LoadOwnershipQualifier::Take)
554
+ return true ;
524
555
break ;
525
- case SILInstructionKind::DebugValueAddrInst:
526
- // Harmless use.
556
+ #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE (Name, ...) \
557
+ case SILInstructionKind::Load##Name##Inst: \
558
+ if (cast<Load##Name##Inst>(inst)->isTake ()) \
559
+ return true ; \
560
+ break ;
561
+ #include " swift/AST/ReferenceStorage.def"
562
+ case SILInstructionKind::OpenExistentialAddrInst:
563
+ // If we have a mutable use, return true. Otherwise fallthrough since we
564
+ // want to look through immutable uses.
565
+ if (cast<OpenExistentialAddrInst>(inst)->getAccessKind () !=
566
+ OpenedExistentialAccess::Immutable)
567
+ return true ;
568
+ LLVM_FALLTHROUGH;
569
+ case SILInstructionKind::StructElementAddrInst:
570
+ case SILInstructionKind::TupleElementAddrInst:
571
+ case SILInstructionKind::IndexAddrInst:
572
+ case SILInstructionKind::TailAddrInst:
573
+ case SILInstructionKind::IndexRawPointerInst:
574
+ // Add these to our worklist.
575
+ for (auto result : inst->getResults ()) {
576
+ copy (result->getUses (), std::back_inserter (worklist));
577
+ }
527
578
break ;
528
579
default :
529
580
llvm_unreachable (" Unhandled unexpected instruction" );
@@ -840,7 +891,19 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
840
891
|| arg->getParent ()->getSinglePredecessorBlock (),
841
892
" Non-branch terminator must have a unique successor." );
842
893
}
894
+ return ;
843
895
}
896
+
897
+ // If we are not in lowered SIL and have an in_guaranteed function argument,
898
+ // verify that we do not mutate or consume it.
899
+ auto *fArg = cast<SILFunctionArgument>(arg);
900
+ if (fArg ->getModule ().getStage () == SILStage::Lowered ||
901
+ !fArg ->getType ().isAddress () ||
902
+ !fArg ->hasConvention (SILArgumentConvention::Indirect_In_Guaranteed))
903
+ return ;
904
+
905
+ require (!ImmutableAddressUseVerifier ().isMutatingOrConsuming (fArg ),
906
+ " Found mutating or consuming use of an in_guaranteed parameter?!" );
844
907
}
845
908
846
909
void visitSILInstruction (SILInstruction *I) {
0 commit comments