@@ -3178,6 +3178,9 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3178
3178
llvm::dbgs () << ' \n ' );
3179
3179
3180
3180
auto subjectTy = S->getSubjectExpr ()->getType ();
3181
+ auto subjectLoweredTy = getLoweredType (subjectTy);
3182
+ auto subjectLoweredAddress =
3183
+ silConv.useLoweredAddresses () && subjectLoweredTy.isAddressOnly (F);
3181
3184
3182
3185
// If the subject expression is uninhabited, we're already dead.
3183
3186
// Emit an unreachable in place of the switch statement.
@@ -3254,27 +3257,88 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3254
3257
emission.emitAddressOnlyAllocations ();
3255
3258
3256
3259
SILBasicBlock *contBB = createBasicBlock ();
3257
- JumpDest contDest (contBB, Cleanups.getCleanupsDepth (), CleanupLocation (S));
3258
-
3260
+ // Depending on the switch ownership behavior, we want to either:
3261
+ //
3262
+ // - allow the subject to (potentially or explicitly) be forwarded into the
3263
+ // case blocks. In this case we want to include the cleanups for the subject
3264
+ // in the case blocks so that the subject and its components can be
3265
+ // consumed by each case block.
3266
+ //
3267
+ // or:
3268
+ //
3269
+ // - evaluate the subject under a formal access scope (a borrow or inout).
3270
+ // In this case the lifetime of the access should cover all the way to the
3271
+ // exits out of the switch.
3272
+ //
3273
+ // When we break out of a case block, we take the subject's remnants with us
3274
+ // in the former case, but not the latter.q
3275
+ CleanupsDepth subjectDepth = Cleanups.getCleanupsDepth ();
3259
3276
LexicalScope switchScope (*this , CleanupLocation (S));
3277
+ llvm::Optional<FormalEvaluationScope> switchFormalAccess;
3278
+
3279
+ bool subjectUndergoesFormalAccess;
3280
+ switch (ownership) {
3281
+ // For normal copyable subjects, we allow the value to be forwarded into
3282
+ // the cases, since we can copy it as needed to evaluate the pattern match.
3283
+ case ValueOwnership::Default:
3284
+ // Similarly, if the subject is an explicitly consumed noncopyable value,
3285
+ // we can forward ownership of the subject's parts into matching case blocks.
3286
+ case ValueOwnership::Owned:
3287
+ subjectUndergoesFormalAccess = false ;
3288
+ break ;
3260
3289
3261
- // Enter a break/continue scope. If we wanted a continue
3262
- // destination, it would probably be out here.
3263
- BreakContinueDestStack.push_back ({S, contDest, JumpDest (S)});
3290
+ // Borrowed and inout pattern matches both undergo a formal access.
3291
+ case ValueOwnership::Shared:
3292
+ case ValueOwnership::InOut:
3293
+ subjectUndergoesFormalAccess = true ;
3294
+ break ;
3295
+ }
3264
3296
3265
3297
PatternMatchContext switchContext = { emission };
3266
3298
SwitchStack.push_back (&switchContext);
3267
3299
3268
- // Emit the subject value. If at +1, dispatching will consume it. If it is at
3269
- // +0, we just forward down borrows.
3270
- //
3271
- // TODO: For an inout switch, we'd start a formal access to an lvalue here.
3272
- ManagedValue subjectMV = emitRValueAsSingleValue (
3273
- S->getSubjectExpr (),
3274
- ownership <= ValueOwnership::Shared
3275
- ? SGFContext::AllowGuaranteedPlusZero
3276
- : SGFContext ());
3277
-
3300
+ // Emit the subject value.
3301
+ ManagedValue subjectMV;
3302
+ switch (ownership) {
3303
+ case ValueOwnership::Default: {
3304
+ // A regular copyable pattern match. Emit as a regular rvalue.
3305
+ // If at +1, dispatching will consume it. If it is at +0, we just forward
3306
+ // down borrows.
3307
+ subjectMV = emitRValueAsSingleValue (
3308
+ S->getSubjectExpr (),
3309
+ SGFContext::AllowGuaranteedPlusZero);
3310
+ break ;
3311
+ }
3312
+ case ValueOwnership::Shared: {
3313
+ // A borrowing pattern match. See if we can emit the subject under a read
3314
+ // formal access.
3315
+ switchFormalAccess.emplace (*this );
3316
+ auto subjectExpr = S->getSubjectExpr ();
3317
+ if (auto subjectLVExpr = findStorageReferenceExprForMoveOnly (subjectExpr,
3318
+ StorageReferenceOperationKind::Borrow)) {
3319
+ LValue sharedLV = emitLValue (subjectLVExpr,
3320
+ subjectLoweredAddress ? SGFAccessKind::BorrowedAddressRead
3321
+ : SGFAccessKind::BorrowedObjectRead);
3322
+ subjectMV = emitBorrowedLValue (S->getSubjectExpr (), std::move (sharedLV));
3323
+ } else {
3324
+ // Emit the value as an allowed-+0 rvalue if it doesn't have special
3325
+ // lvalue treatment.
3326
+ subjectMV = emitRValueAsSingleValue (S->getSubjectExpr (),
3327
+ SGFContext::AllowGuaranteedPlusZero);
3328
+ }
3329
+ break ;
3330
+ }
3331
+ case ValueOwnership::InOut: {
3332
+ // A mutating pattern match. Emit the subject under a modify access.
3333
+ llvm_unreachable (" not yet implemented" );
3334
+ }
3335
+ case ValueOwnership::Owned: {
3336
+ // A consuming pattern match. Emit as a +1 rvalue.
3337
+ subjectMV = emitRValueAsSingleValue (S->getSubjectExpr ());
3338
+ break ;
3339
+ }
3340
+ }
3341
+
3278
3342
// Inline constructor for subject.
3279
3343
auto subject = ([&]() -> ConsumableManagedValue {
3280
3344
if (subjectMV.getType ().isMoveOnly ()) {
@@ -3292,12 +3356,15 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3292
3356
3293
3357
case ValueOwnership::Shared:
3294
3358
emission.setNoncopyableBorrowingOwnership ();
3359
+ if (!subjectMV.isPlusZero ()) {
3360
+ subjectMV = subjectMV.borrow (*this , S);
3361
+ }
3295
3362
if (subjectMV.getType ().isAddress () &&
3296
3363
subjectMV.getType ().isLoadable (F)) {
3297
3364
// Load a borrow if the type is loadable.
3298
- subjectMV = B. createLoadBorrow (S, subjectMV);
3299
- } else if (!subjectMV. isPlusZero ()) {
3300
- subjectMV = subjectMV. borrow (* this , S );
3365
+ subjectMV = subjectUndergoesFormalAccess
3366
+ ? B. createFormalAccessLoadBorrow (S, subjectMV)
3367
+ : B. createLoadBorrow (S, subjectMV );
3301
3368
}
3302
3369
return {subjectMV, CastConsumptionKind::BorrowAlways};
3303
3370
@@ -3385,6 +3452,20 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3385
3452
return {subjectMV.copy (*this , S), CastConsumptionKind::TakeAlways};
3386
3453
}());
3387
3454
3455
+ CleanupsDepth caseBodyDepth = Cleanups.getCleanupsDepth ();
3456
+ llvm::Optional<LexicalScope> caseBodyScope;
3457
+ if (subjectUndergoesFormalAccess) {
3458
+ caseBodyScope.emplace (*this , CleanupLocation (S));
3459
+ }
3460
+
3461
+ // Enter a break/continue scope. As discussed above, the depth we jump to
3462
+ // depends on whether the subject is under a formal access.
3463
+ JumpDest contDest (contBB,
3464
+ subjectUndergoesFormalAccess ? caseBodyDepth
3465
+ : subjectDepth,
3466
+ CleanupLocation (S));
3467
+ BreakContinueDestStack.push_back ({S, contDest, JumpDest (S)});
3468
+
3388
3469
// If we need to diagnose an unexpected enum case or unexpected enum case
3389
3470
// value, we need access to a value metatype for the subject. Emit this state
3390
3471
// now before we emit the actual switch to ensure that the subject has not
@@ -3446,7 +3527,14 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3446
3527
}
3447
3528
3448
3529
assert (!B.hasValidInsertionPoint ());
3449
- switchScope.pop ();
3530
+ // Disable the cleanups for values that should be consumed by the case
3531
+ // bodies.
3532
+ caseBodyScope.reset ();
3533
+ // If the subject isn't under a formal access, that includes the subject
3534
+ // itself.
3535
+ if (!subjectUndergoesFormalAccess) {
3536
+ switchScope.pop ();
3537
+ }
3450
3538
3451
3539
// Then emit the case blocks shared by multiple pattern cases.
3452
3540
emission.emitSharedCaseBlocks (
@@ -3463,6 +3551,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3463
3551
} else {
3464
3552
B.emitBlock (contBB);
3465
3553
}
3554
+
3555
+ // End the formal access to the subject now (if there was one).
3556
+ if (subjectUndergoesFormalAccess) {
3557
+ switchFormalAccess.reset ();
3558
+ switchScope.pop ();
3559
+ }
3466
3560
3467
3561
// Now that we have emitted everything, see if our unexpected enum case info
3468
3562
// metatypes were actually used. If not, delete them.
0 commit comments