@@ -1186,11 +1186,49 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
1186
1186
}
1187
1187
}
1188
1188
1189
- if (OwnershipForwardingMixin::isa (I)) {
1189
+ if (I-> getFunction ()-> hasOwnership () && OwnershipForwardingMixin::isa (I)) {
1190
1190
checkOwnershipForwardingInst (I);
1191
1191
}
1192
1192
}
1193
1193
1194
+ // Verify the result of any forwarding terminator, including switch_enum.
1195
+ void checkForwardedTermResult (OwnershipForwardingTermInst *term,
1196
+ SILBasicBlock *destBB) {
1197
+ require (destBB->getNumArguments () == 1 ,
1198
+ " OwnershipForwardingTermInst needs a single result" );
1199
+ auto *arg = destBB->getArgument (0 );
1200
+ auto argKind = arg->getOwnershipKind ();
1201
+ // A terminator may cast a nontrivial to a trivial type. e.g. a trivial
1202
+ // payload in a nontrivial enum. If the result is trivial, it no longer
1203
+ // requires ownership.
1204
+ if (arg->getType ().isTrivial (F) && argKind == OwnershipKind::None)
1205
+ return ;
1206
+
1207
+ require (argKind == term->getForwardingOwnershipKind (),
1208
+ " OwnershipForwardingTermInst nontrivial result "
1209
+ " must have the same ownership" );
1210
+ }
1211
+
1212
+ // / A terminator result is forwarded from the terminator's operand. Forwarding
1213
+ // / a nontrivial value to another nontrivial value can never gain or lose
1214
+ // / ownership information. If the terminator's operand has ownership and the
1215
+ // / result is nontrivial, then the result must have identical ownership.
1216
+ // /
1217
+ // / The terminator result can only "lose" ownership if the operand is
1218
+ // / nontrivial but the result is trivial. It is still valid for the trivial
1219
+ // / result to retain the operand's ownership, but unnecessary and produces
1220
+ // / less efficient SIL.
1221
+ void checkOwnershipForwardingTermInst (OwnershipForwardingTermInst *term) {
1222
+ // Verifying switch_enum ownership requires evaluating the payload
1223
+ // types. These are fully verified by checkSwitchEnumInst.
1224
+ if (isa<SwitchEnumInst>(term))
1225
+ return ;
1226
+
1227
+ for (auto &succ : term->getSuccessors ()) {
1228
+ checkForwardedTermResult (term, succ.getBB ());
1229
+ }
1230
+ }
1231
+
1194
1232
// / We are given an instruction \p fInst that forwards ownership from \p
1195
1233
// / operand to one of \p fInst's results, make sure that if we have a
1196
1234
// / forwarding instruction that can only accept owned or guaranteed ownership
@@ -1209,6 +1247,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
1209
1247
" GuaranteedFirstArgForwardingSingleValueInst's ownership kind "
1210
1248
" must be compatible with guaranteed" );
1211
1249
}
1250
+
1251
+ if (auto *term = dyn_cast<OwnershipForwardingTermInst>(i)) {
1252
+ checkOwnershipForwardingTermInst (term);
1253
+ }
1212
1254
}
1213
1255
1214
1256
void checkDebugVariable (SILInstruction *inst) {
@@ -4503,104 +4545,96 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
4503
4545
checkSelectValueCases (SVI);
4504
4546
}
4505
4547
4506
- void checkSwitchEnumInst (SwitchEnumInst *SOI ) {
4507
- require (SOI ->getOperand ()->getType ().isObject (),
4548
+ void checkSwitchEnumInst (SwitchEnumInst *switchEnum ) {
4549
+ require (switchEnum ->getOperand ()->getType ().isObject (),
4508
4550
" switch_enum operand must be an object" );
4509
4551
4510
- SILType uTy = SOI ->getOperand ()->getType ();
4552
+ SILType uTy = switchEnum ->getOperand ()->getType ();
4511
4553
EnumDecl *uDecl = uTy.getEnumOrBoundGenericEnum ();
4512
4554
require (uDecl, " switch_enum operand is not an enum" );
4513
4555
4514
4556
// Find the set of enum elements for the type so we can verify
4515
4557
// exhaustiveness.
4516
4558
llvm::DenseSet<EnumElementDecl*> unswitchedElts;
4517
- uDecl->getAllElements (unswitchedElts);
4518
-
4519
- // Verify the set of enum cases we dispatch on.
4520
- for (unsigned i = 0 , e = SOI->getNumCases (); i < e; ++i) {
4521
- EnumElementDecl *elt;
4522
- SILBasicBlock *dest;
4523
- std::tie (elt, dest) = SOI->getCase (i);
4524
4559
4560
+ auto checkSwitchCase = [&](EnumElementDecl *elt, SILBasicBlock *dest) {
4525
4561
require (elt->getDeclContext () == uDecl,
4526
4562
" switch_enum dispatches on enum element that is not part of "
4527
4563
" its type" );
4528
- require (unswitchedElts.count (elt),
4564
+ require (unswitchedElts.insert (elt). second ,
4529
4565
" switch_enum dispatches on same enum element more than once" );
4530
- unswitchedElts.erase (elt);
4531
-
4532
- // The destination BB can take the argument payload, if any, as a BB
4533
- // arguments, or it can ignore it and take no arguments.
4534
- if (elt->hasAssociatedValues ()) {
4535
- if (isSILOwnershipEnabled () && F.hasOwnership ()) {
4536
- require (dest->getArguments ().size () == 1 ,
4537
- " switch_enum destination for case w/ args must take 1 "
4538
- " argument" );
4539
- if (!dest->getArgument (0 )->getType ().isTrivial (*SOI->getFunction ())) {
4540
- require (
4541
- dest->getArgument (0 )->getOwnershipKind ().isCompatibleWith (
4542
- SOI->getForwardingOwnershipKind ()),
4543
- " Switch enum non-trivial destination arg must have ownership "
4544
- " kind that is compatible with the switch_enum's operand" );
4545
- }
4546
- } else {
4547
- require (dest->getArguments ().empty () ||
4548
- dest->getArguments ().size () == 1 ,
4549
- " switch_enum destination for case w/ args must take 0 or 1 "
4550
- " arguments" );
4551
- }
4552
-
4553
- if (dest->getArguments ().size () == 1 ) {
4554
- SILType eltArgTy = uTy.getEnumElementType (elt, F.getModule (),
4555
- F.getTypeExpansionContext ());
4556
- SILType bbArgTy = dest->getArguments ()[0 ]->getType ();
4557
- if (F.getModule ().getStage () != SILStage::Lowered) {
4558
- // During the lowered stage, a function type might have different
4559
- // signature
4560
- require (eltArgTy == bbArgTy,
4561
- " switch_enum destination bbarg must match case arg type" );
4562
- }
4563
- require (!dest->getArguments ()[0 ]->getType ().isAddress (),
4564
- " switch_enum destination bbarg type must not be an address" );
4565
- }
4566
4566
4567
- } else {
4567
+ if (!elt-> hasAssociatedValues ()) {
4568
4568
require (dest->getArguments ().empty (),
4569
4569
" switch_enum destination for no-argument case must take no "
4570
4570
" arguments" );
4571
+ return ;
4572
+ }
4573
+ // Check for a valid switch result type.
4574
+ if (dest->getArguments ().size () == 1 ) {
4575
+ SILType eltArgTy = uTy.getEnumElementType (elt, F.getModule (),
4576
+ F.getTypeExpansionContext ());
4577
+ SILType bbArgTy = dest->getArguments ()[0 ]->getType ();
4578
+ if (F.getModule ().getStage () != SILStage::Lowered) {
4579
+ // During the lowered stage, a function type might have different
4580
+ // signature
4581
+ require (eltArgTy == bbArgTy,
4582
+ " switch_enum destination bbarg must match case arg type" );
4583
+ }
4584
+ require (!dest->getArguments ()[0 ]->getType ().isAddress (),
4585
+ " switch_enum destination bbarg type must not be an address" );
4586
+ }
4587
+ if (!isSILOwnershipEnabled () || !F.hasOwnership ()) {
4588
+ // In non-OSSA, the destBB can optionally ignore the payload.
4589
+ require (dest->getArguments ().empty ()
4590
+ || dest->getArguments ().size () == 1 ,
4591
+ " switch_enum destination for case w/ args must take 0 or 1 "
4592
+ " arguments" );
4593
+ return ;
4571
4594
}
4595
+ checkForwardedTermResult (switchEnum, dest);
4596
+ };
4597
+ // Verify the set of enum cases we dispatch on.
4598
+ for (unsigned i = 0 , e = switchEnum->getNumCases (); i < e; ++i) {
4599
+ EnumElementDecl *elt;
4600
+ SILBasicBlock *dest;
4601
+ std::tie (elt, dest) = switchEnum->getCase (i);
4602
+ checkSwitchCase (elt, dest);
4572
4603
}
4573
-
4574
4604
// If the switch is non-exhaustive, we require a default.
4575
- bool isExhaustive =
4576
- uDecl->isEffectivelyExhaustive (F.getModule ().getSwiftModule (),
4577
- F.getResilienceExpansion ());
4578
- require ((isExhaustive && unswitchedElts.empty ()) || SOI->hasDefault (),
4579
- " nonexhaustive switch_enum must have a default destination" );
4580
- if (SOI->hasDefault ()) {
4581
- // When SIL ownership is enabled, we require all default branches to take
4582
- // an @owned original version of the enum.
4583
- //
4584
- // When SIL ownership is disabled, we no longer support this.
4585
- if (isSILOwnershipEnabled () && F.hasOwnership ()) {
4586
- require (SOI->getDefaultBB ()->getNumArguments () == 1 ,
4587
- " Switch enum default block should have one argument" );
4588
- requireSameType (
4589
- SOI->getDefaultBB ()->getArgument (0 )->getType (),
4590
- SOI->getOperand ()->getType (),
4591
- " Switch enum default block should have one argument that is "
4592
- " the same as the input type" );
4593
- auto defaultKind =
4594
- SOI->getDefaultBB ()->getArgument (0 )->getOwnershipKind ();
4595
- require (
4596
- defaultKind.isCompatibleWith (SOI->getOperand ().getOwnershipKind ()),
4597
- " Switch enum default block arg must have same ownership kind "
4598
- " as operand" );
4599
- } else if (!F.hasOwnership ()) {
4600
- require (SOI->getDefaultBB ()->args_empty (),
4601
- " switch_enum default destination must take no arguments" );
4602
- }
4605
+ if (!switchEnum->hasDefault ()) {
4606
+ bool isExhaustive = uDecl->isEffectivelyExhaustive (
4607
+ F.getModule ().getSwiftModule (), F.getResilienceExpansion ());
4608
+ require (isExhaustive
4609
+ && (unswitchedElts.size () == uDecl->getNumElements ()),
4610
+ " nonexhaustive switch_enum must have a default destination" );
4611
+ return ;
4612
+ }
4613
+ auto *defaultBB = switchEnum->getDefaultBB ();
4614
+ if (!isSILOwnershipEnabled () || !F.hasOwnership ()) {
4615
+ require (switchEnum->getDefaultBB ()->args_empty (),
4616
+ " switch_enum default destination must take no arguments" );
4617
+ return ;
4603
4618
}
4619
+ // When the switch has a unique default case, the OSSA result has the same
4620
+ // requirements as a matched result.
4621
+ if (NullablePtr<EnumElementDecl> uniqueCase =
4622
+ switchEnum->getUniqueCaseForDefault ()) {
4623
+ checkSwitchCase (uniqueCase.get (), defaultBB);
4624
+ return ;
4625
+ }
4626
+ // With no unique case, the switch_enum operand is simply forwarded.
4627
+ require (defaultBB->getNumArguments () == 1 ,
4628
+ " Switch enum default block should have one argument" );
4629
+ requireSameType (
4630
+ defaultBB->getArgument (0 )->getType (),
4631
+ switchEnum->getOperand ()->getType (),
4632
+ " Switch enum default block should have one argument that is "
4633
+ " the same as the input type" );
4634
+ require (defaultBB->getArgument (0 )->getOwnershipKind ()
4635
+ == switchEnum->getForwardingOwnershipKind (),
4636
+ " switch_enum non-trivial destination arg must have the same "
4637
+ " ownership as switch_enum's operand" );
4604
4638
}
4605
4639
4606
4640
void checkSwitchEnumAddrInst (SwitchEnumAddrInst *SOI) {
0 commit comments