@@ -3218,12 +3218,123 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
3218
3218
SGF.Cleanups .emitBranchAndCleanups (sharedDest, caseBlock, args);
3219
3219
}
3220
3220
3221
+ // TODO: Integrate this with findStorageExprForMoveOnly, which does almost the
3222
+ // same check.
3223
+ static bool isBorrowableSubject (SILGenFunction &SGF,
3224
+ Expr *subjectExpr) {
3225
+ // Look through forwarding expressions.
3226
+ for (;;) {
3227
+ subjectExpr = subjectExpr->getValueProvidingExpr ();
3228
+
3229
+ // Look through loads.
3230
+ if (auto load = dyn_cast<LoadExpr>(subjectExpr)) {
3231
+ subjectExpr = load->getSubExpr ();
3232
+ continue ;
3233
+ }
3234
+
3235
+ // Look through optional force-projections.
3236
+ // We can't look through optional evaluations here because wrapping the
3237
+ // value up in an Optional at the end needs a copy/move to create the
3238
+ // temporary optional.
3239
+ if (auto force = dyn_cast<ForceValueExpr>(subjectExpr)) {
3240
+ subjectExpr = force->getSubExpr ();
3241
+ continue ;
3242
+ }
3243
+
3244
+ // Look through parens.
3245
+ if (auto paren = dyn_cast<ParenExpr>(subjectExpr)) {
3246
+ subjectExpr = paren->getSubExpr ();
3247
+ continue ;
3248
+ }
3249
+
3250
+ // Look through `try` and `await`.
3251
+ if (auto tryExpr = dyn_cast<TryExpr>(subjectExpr)) {
3252
+ subjectExpr = tryExpr->getSubExpr ();
3253
+ continue ;
3254
+ }
3255
+ if (auto awaitExpr = dyn_cast<AwaitExpr>(subjectExpr)) {
3256
+ subjectExpr = awaitExpr->getSubExpr ();
3257
+ continue ;
3258
+ }
3259
+
3260
+ break ;
3261
+ }
3262
+
3263
+ // An explicit `borrow` expression requires us to do a borrowing access.
3264
+ if (isa<BorrowExpr>(subjectExpr)) {
3265
+ return true ;
3266
+ }
3267
+
3268
+ AbstractStorageDecl *storage;
3269
+ AccessSemantics access ;
3270
+
3271
+ // A subject is potentially borrowable if it's some kind of storage reference.
3272
+ if (auto declRef = dyn_cast<DeclRefExpr>(subjectExpr)) {
3273
+ storage = dyn_cast<AbstractStorageDecl>(declRef->getDecl ());
3274
+ access = declRef->getAccessSemantics ();
3275
+ } else if (auto memberRef = dyn_cast<MemberRefExpr>(subjectExpr)) {
3276
+ storage = dyn_cast<AbstractStorageDecl>(memberRef->getMember ().getDecl ());
3277
+ access = memberRef->getAccessSemantics ();
3278
+ } else if (auto subscript = dyn_cast<SubscriptExpr>(subjectExpr)) {
3279
+ storage = dyn_cast<AbstractStorageDecl>(subscript->getMember ().getDecl ());
3280
+ access = subscript->getAccessSemantics ();
3281
+ } else {
3282
+ return false ;
3283
+ }
3284
+
3285
+ // If the member being referenced isn't storage, there's no benefit to
3286
+ // borrowing it.
3287
+ if (!storage) {
3288
+ return false ;
3289
+ }
3290
+
3291
+ // Check the access strategy used to read the storage.
3292
+ auto strategy = storage->getAccessStrategy (access ,
3293
+ AccessKind::Read,
3294
+ SGF.SGM .SwiftModule ,
3295
+ SGF.F .getResilienceExpansion ());
3296
+
3297
+ switch (strategy.getKind ()) {
3298
+ case AccessStrategy::Kind::Storage:
3299
+ // Accessing storage directly benefits from borrowing.
3300
+ return true ;
3301
+ case AccessStrategy::Kind::DirectToAccessor:
3302
+ case AccessStrategy::Kind::DispatchToAccessor:
3303
+ // If we use an accessor, the kind of accessor affects whether we get
3304
+ // a reference we can borrow or a temporary that will be consumed.
3305
+ switch (strategy.getAccessor ()) {
3306
+ case AccessorKind::Get:
3307
+ case AccessorKind::DistributedGet:
3308
+ // Get returns an owned value.
3309
+ return false ;
3310
+ case AccessorKind::Read:
3311
+ case AccessorKind::Modify:
3312
+ case AccessorKind::Address:
3313
+ case AccessorKind::MutableAddress:
3314
+ // Read, modify, and addressors yield a borrowable reference.
3315
+ return true ;
3316
+ case AccessorKind::Init:
3317
+ case AccessorKind::Set:
3318
+ case AccessorKind::WillSet:
3319
+ case AccessorKind::DidSet:
3320
+ llvm_unreachable (" should not be involved in a read" );
3321
+ }
3322
+ llvm_unreachable (" switch not covered?" );
3323
+
3324
+ case AccessStrategy::Kind::MaterializeToTemporary:
3325
+ case AccessStrategy::Kind::DispatchToDistributedThunk:
3326
+ return false ;
3327
+ }
3328
+ llvm_unreachable (" switch not covered?" );
3329
+ }
3330
+
3221
3331
void SILGenFunction::emitSwitchStmt (SwitchStmt *S) {
3222
3332
LLVM_DEBUG (llvm::dbgs () << " emitting switch stmt\n " ;
3223
3333
S->dump (llvm::dbgs ());
3224
3334
llvm::dbgs () << ' \n ' );
3225
3335
3226
- auto subjectTy = S->getSubjectExpr ()->getType ();
3336
+ auto subjectExpr = S->getSubjectExpr ();
3337
+ auto subjectTy = subjectExpr->getType ();
3227
3338
auto subjectLoweredTy = getLoweredType (subjectTy);
3228
3339
auto subjectLoweredAddress =
3229
3340
silConv.useLoweredAddresses () && subjectLoweredTy.isAddressOnly (F);
@@ -3244,7 +3355,14 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
3244
3355
if (getASTContext ().LangOpts .hasFeature (Feature::BorrowingSwitch)) {
3245
3356
if (subjectTy->isNoncopyable ()) {
3246
3357
// Determine the overall ownership behavior of the switch, based on the
3247
- // patterns' ownership behavior.
3358
+ // subject expression and the patterns' ownership behavior.
3359
+
3360
+ // If the subject expression is borrowable, then perform the switch as
3361
+ // a borrow. (A `consume` expression would render the expression
3362
+ // non-borrowable.) Otherwise, perform it as a consume.
3363
+ ownership = isBorrowableSubject (*this , subjectExpr)
3364
+ ? ValueOwnership::Shared
3365
+ : ValueOwnership::Owned;
3248
3366
for (auto caseLabel : S->getCases ()) {
3249
3367
for (auto item : caseLabel->getCaseLabelItems ()) {
3250
3368
ownership = std::max (ownership, item.getPattern ()->getOwnership ());
0 commit comments