@@ -3218,12 +3218,123 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
32183218 SGF.Cleanups .emitBranchAndCleanups (sharedDest, caseBlock, args);
32193219}
32203220
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+
32213331void SILGenFunction::emitSwitchStmt (SwitchStmt *S) {
32223332 LLVM_DEBUG (llvm::dbgs () << " emitting switch stmt\n " ;
32233333 S->dump (llvm::dbgs ());
32243334 llvm::dbgs () << ' \n ' );
32253335
3226- auto subjectTy = S->getSubjectExpr ()->getType ();
3336+ auto subjectExpr = S->getSubjectExpr ();
3337+ auto subjectTy = subjectExpr->getType ();
32273338 auto subjectLoweredTy = getLoweredType (subjectTy);
32283339 auto subjectLoweredAddress =
32293340 silConv.useLoweredAddresses () && subjectLoweredTy.isAddressOnly (F);
@@ -3244,7 +3355,14 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
32443355 if (getASTContext ().LangOpts .hasFeature (Feature::BorrowingSwitch)) {
32453356 if (subjectTy->isNoncopyable ()) {
32463357 // 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;
32483366 for (auto caseLabel : S->getCases ()) {
32493367 for (auto item : caseLabel->getCaseLabelItems ()) {
32503368 ownership = std::max (ownership, item.getPattern ()->getOwnership ());
0 commit comments