@@ -174,6 +174,15 @@ static bool compatibleOwnershipKinds(ValueOwnershipKind K1,
174
174
return K1.merge (K2).hasValue ();
175
175
}
176
176
177
+ // / Returns true if \p Kind is trivial or if \p Kind is compatible with \p
178
+ // / ComparisonKind.
179
+ static bool
180
+ trivialOrCompatibleOwnershipKinds (ValueOwnershipKind Kind,
181
+ ValueOwnershipKind ComparisonKind) {
182
+ return compatibleOwnershipKinds (Kind, ValueOwnershipKind::Trivial) ||
183
+ compatibleOwnershipKinds (Kind, ComparisonKind);
184
+ }
185
+
177
186
static bool isValueAddressOrTrivial (SILValue V, SILModule &M) {
178
187
return V->getType ().isAddress () ||
179
188
V.getOwnershipKind () == ValueOwnershipKind::Trivial ||
@@ -198,6 +207,8 @@ static bool isOwnershipForwardingValueKind(ValueKind K) {
198
207
case ValueKind::UncheckedEnumDataInst:
199
208
case ValueKind::MarkUninitializedInst:
200
209
case ValueKind::SelectEnumInst:
210
+ case ValueKind::SwitchEnumInst:
211
+ case ValueKind::CheckedCastBranchInst:
201
212
return true ;
202
213
default :
203
214
return false ;
@@ -327,6 +338,12 @@ class OwnershipCompatibilityUseChecker
327
338
OwnershipUseCheckerResult visitForwardingInst (SILInstruction *I) {
328
339
return visitForwardingInst (I, I->getAllOperands ());
329
340
}
341
+
342
+ // / Visit a terminator instance that performs a transform like
343
+ // / operation. E.x.: switch_enum, checked_cast_br. This does not include br or
344
+ // / cond_br.
345
+ OwnershipUseCheckerResult visitTransformingTerminatorInst (TermInst *TI);
346
+
330
347
OwnershipUseCheckerResult
331
348
visitApplyArgument (ValueOwnershipKind RequiredConvention, bool ShouldCheck);
332
349
OwnershipUseCheckerResult
@@ -420,7 +437,6 @@ NO_OPERAND_INST(KeyPath)
420
437
return {compatibleWithOwnership (ValueOwnershipKind::OWNERSHIP), \
421
438
SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS}; \
422
439
}
423
- CONSTANT_OWNERSHIP_INST (Guaranteed, true , EndBorrowArgument)
424
440
CONSTANT_OWNERSHIP_INST (Guaranteed, false , RefElementAddr)
425
441
CONSTANT_OWNERSHIP_INST(Owned, true , AutoreleaseValue)
426
442
CONSTANT_OWNERSHIP_INST(Owned, true , DeallocBox)
@@ -511,9 +527,7 @@ CONSTANT_OWNERSHIP_INST(Trivial, false, DeallocValueBuffer)
511
527
return {compatibleWithOwnership (ValueOwnershipKind::OWNERSHIP), \
512
528
SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS}; \
513
529
}
514
- CONSTANT_OR_TRIVIAL_OWNERSHIP_INST (Owned, true , CheckedCastBranch)
515
530
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST (Owned, true , CheckedCastValueBranch)
516
- CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true , SwitchEnum)
517
531
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true , InitExistentialOpaque)
518
532
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true , DeinitExistentialOpaque)
519
533
#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
@@ -655,6 +669,17 @@ FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, TupleExtract)
655
669
FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false , StructExtract)
656
670
#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
657
671
672
+ OwnershipUseCheckerResult
673
+ OwnershipCompatibilityUseChecker::visitEndBorrowArgumentInst (EndBorrowArgumentInst *I) {
674
+ // If we are currently checking an end_borrow_argument as a subobject, then we
675
+ // treat this as just a use.
676
+ if (isCheckingSubObject ())
677
+ return {true , false };
678
+
679
+ // Otherwise, we must be checking an actual argument. Make sure it is guaranteed!
680
+ return {true , compatibleWithOwnership (ValueOwnershipKind::Guaranteed)};
681
+ }
682
+
658
683
OwnershipUseCheckerResult
659
684
OwnershipCompatibilityUseChecker::visitSelectEnumInst (SelectEnumInst *I) {
660
685
if (getValue () == I->getEnumOperand ()) {
@@ -724,6 +749,59 @@ OwnershipCompatibilityUseChecker::visitCondBranchInst(CondBranchInst *CBI) {
724
749
getOperandIndex () - FalseOffset);
725
750
}
726
751
752
+ OwnershipUseCheckerResult
753
+ OwnershipCompatibilityUseChecker::visitSwitchEnumInst (SwitchEnumInst *SEI) {
754
+ return visitTransformingTerminatorInst (SEI);
755
+ }
756
+
757
+ OwnershipUseCheckerResult
758
+ OwnershipCompatibilityUseChecker::visitCheckedCastBranchInst (
759
+ CheckedCastBranchInst *SEI) {
760
+ return visitTransformingTerminatorInst (SEI);
761
+ }
762
+
763
+ OwnershipUseCheckerResult
764
+ OwnershipCompatibilityUseChecker::visitTransformingTerminatorInst (
765
+ TermInst *TI) {
766
+ // If our operand was trivial, return early.
767
+ if (compatibleWithOwnership (ValueOwnershipKind::Trivial))
768
+ return {true , false };
769
+
770
+ // Then we need to go through all of our destinations and make sure that if
771
+ // they have a payload, the payload's convention matches our
772
+ // convention.
773
+ //
774
+ // *NOTE* we assume that all of our types line up and are checked by the
775
+ // normal verifier.
776
+ for (auto *Succ : TI->getParent ()->getSuccessorBlocks ()) {
777
+ // This must be a no-payload case... continue.
778
+ if (Succ->args_size () == 0 )
779
+ continue ;
780
+
781
+ // If we have a trivial value or a value with ownership kind that matches
782
+ // the switch_enum, then continue.
783
+ auto OwnershipKind = Succ->getArgument (0 )->getOwnershipKind ();
784
+ if (OwnershipKind == ValueOwnershipKind::Trivial ||
785
+ compatibleWithOwnership (OwnershipKind))
786
+ continue ;
787
+
788
+ // Otherwise, emit an error.
789
+ handleError ([&]() {
790
+ llvm::errs ()
791
+ << " Function: '" << Succ->getParent ()->getName () << " '\n "
792
+ << " Error! Argument ownership kind does not match terminator!\n "
793
+ << " Terminator: " << *TI << " Argument: " << *Succ->getArgument (0 )
794
+ << " Expected convention: " << getOwnershipKind () << " .\n "
795
+ << " Actual convention: " << OwnershipKind << ' \n '
796
+ << ' \n ' ;
797
+ });
798
+ }
799
+
800
+ // Finally, if everything lines up, emit that we match and are a lifetime
801
+ // ending point if we are owned.
802
+ return {true , compatibleWithOwnership (ValueOwnershipKind::Owned)};
803
+ }
804
+
727
805
OwnershipUseCheckerResult
728
806
OwnershipCompatibilityUseChecker::visitReturnInst (ReturnInst *RI) {
729
807
SILModule &M = RI->getModule ();
@@ -1454,8 +1532,8 @@ void SILValueOwnershipChecker::gatherUsers(
1454
1532
// we need to look through subobject uses for more uses. Otherwise, if we are
1455
1533
// forwarding, we do not create any lifetime ending users/non lifetime ending
1456
1534
// users since we verify against our base.
1457
- bool IsGuaranteed =
1458
- Value. getOwnershipKind () == ValueOwnershipKind::Guaranteed;
1535
+ auto OwnershipKind = Value. getOwnershipKind ();
1536
+ bool IsGuaranteed = OwnershipKind == ValueOwnershipKind::Guaranteed;
1459
1537
1460
1538
if (IsGuaranteed && isOwnershipForwardingValue (Value))
1461
1539
return ;
@@ -1512,19 +1590,63 @@ void SILValueOwnershipChecker::gatherUsers(
1512
1590
1513
1591
// At this point, we know that we must have a forwarded subobject. Since the
1514
1592
// base type is guaranteed, we know that the subobject is either guaranteed
1515
- // or trivial. The trivial case is not interesting for ARC verification, so
1516
- // if the user has a trivial ownership kind, continue.
1517
- if (SILValue (User).getOwnershipKind () == ValueOwnershipKind::Trivial) {
1593
+ // or trivial. We now split into two cases, if the user is a terminator or
1594
+ // not. If we do not have a terminator, then just add User->getUses() to the
1595
+ // worklist.
1596
+ auto *TI = dyn_cast<TermInst>(User);
1597
+ if (!TI) {
1598
+ if (SILValue (User).getOwnershipKind () == ValueOwnershipKind::Trivial) {
1599
+ continue ;
1600
+ }
1601
+
1602
+ // Now, we /must/ have a guaranteed subobject, so lets assert that the
1603
+ // user
1604
+ // is actually guaranteed and add the subobject's users to our worklist.
1605
+ assert (SILValue (User).getOwnershipKind () ==
1606
+ ValueOwnershipKind::Guaranteed &&
1607
+ " Our value is guaranteed and this is a forwarding instruction. "
1608
+ " Should have guaranteed ownership as well." );
1609
+ std::copy (User->use_begin (), User->use_end (), std::back_inserter (Users));
1518
1610
continue ;
1519
1611
}
1520
1612
1521
- // Now, we /must/ have a guaranteed subobject, so lets assert that the user
1522
- // is actually guaranteed and add the subobject's users to our worklist.
1523
- assert (SILValue (User).getOwnershipKind () ==
1524
- ValueOwnershipKind::Guaranteed &&
1525
- " Our value is guaranteed and this is a forwarding instruction. "
1526
- " Should have guaranteed ownership as well." );
1527
- std::copy (User->use_begin (), User->use_end (), std::back_inserter (Users));
1613
+ // Otherwise if we have a terminator, add any as uses any
1614
+ // end_borrow_argument to ensure that the subscope is completely enclsed
1615
+ // within the super scope. all of the arguments to the work list. We require
1616
+ // all of our arguments to be either trivial or guaranteed.
1617
+ for (auto &Succ : TI->getSuccessors ()) {
1618
+ auto *BB = Succ.getBB ();
1619
+
1620
+ // If we do not have any arguments, then continue.
1621
+ if (BB->args_empty ())
1622
+ continue ;
1623
+
1624
+ // Otherwise, make sure that all arguments are trivial or guaranteed. If
1625
+ // we fail, emit an error.
1626
+ //
1627
+ // TODO: We could ignore this error and emit a more specific error on the
1628
+ // actual terminator.
1629
+ for (auto *BBArg : BB->getArguments ()) {
1630
+ // *NOTE* We do not emit an error here since we want to allow for more
1631
+ // specific errors to be found during use_verification.
1632
+ //
1633
+ // TODO: Add a flag that associates the terminator instruction with
1634
+ // needing to be verified. If it isn't verified appropriately, assert
1635
+ // when the verifier is destroyed.
1636
+ if (!trivialOrCompatibleOwnershipKinds (BBArg->getOwnershipKind (),
1637
+ OwnershipKind)) {
1638
+ // This is where the error would go.
1639
+ continue ;
1640
+ }
1641
+
1642
+ // If we have a trivial value, just continue.
1643
+ if (BBArg->getOwnershipKind () == ValueOwnershipKind::Trivial)
1644
+ continue ;
1645
+
1646
+ // Otherwise,
1647
+ std::copy (BBArg->use_begin (), BBArg->use_end (), std::back_inserter (Users));
1648
+ }
1649
+ }
1528
1650
}
1529
1651
}
1530
1652
0 commit comments