@@ -2939,7 +2939,48 @@ static void emitDelayedArguments(SILGenFunction &SGF,
29392939 }
29402940}
29412941
2942- static Expr *findStorageReferenceExprForBorrow (Expr *e) {
2942+ namespace {
2943+ // / Container to hold the result of a search for the storage reference
2944+ // / when determining to emit a borrow.
2945+ struct StorageRefResult {
2946+ private:
2947+ Expr *storageRef;
2948+ Expr *transitiveRoot;
2949+
2950+ public:
2951+ // Represents an empty result
2952+ StorageRefResult () : storageRef(nullptr ), transitiveRoot(nullptr ) {}
2953+ bool isEmpty () const { return transitiveRoot == nullptr ; }
2954+ operator bool () const { return !isEmpty (); }
2955+
2956+ // / The root of the expression that accesses the storage in \c storageRef.
2957+ // / When in doubt, this is probably what you want, as it includes the
2958+ // / entire expression tree involving the reference.
2959+ Expr *getTransitiveRoot () const { return transitiveRoot; }
2960+
2961+ // / The direct storage reference that was discovered.
2962+ Expr *getStorageRef () const { return storageRef; }
2963+
2964+ StorageRefResult (Expr *storageRef, Expr *transitiveRoot)
2965+ : storageRef(storageRef), transitiveRoot(transitiveRoot) {
2966+ assert (storageRef && transitiveRoot && " use the zero-arg init for empty" );
2967+ }
2968+
2969+ // Initializes a storage reference where the base matches the ref.
2970+ StorageRefResult (Expr *storageRef)
2971+ : StorageRefResult(storageRef, storageRef) {}
2972+
2973+ StorageRefResult withTransitiveRoot (StorageRefResult refResult) const {
2974+ return withTransitiveRoot (refResult.transitiveRoot );
2975+ }
2976+
2977+ StorageRefResult withTransitiveRoot (Expr *newRoot) const {
2978+ return StorageRefResult (storageRef, newRoot);
2979+ }
2980+ };
2981+ } // namespace
2982+
2983+ static StorageRefResult findStorageReferenceExprForBorrow (Expr *e) {
29432984 e = e->getSemanticsProvidingExpr ();
29442985
29452986 // These are basically defined as the cases implemented by SILGenLValue.
@@ -2962,71 +3003,100 @@ static Expr *findStorageReferenceExprForBorrow(Expr *e) {
29623003 // sub-expression is a storage reference, but don't return the
29633004 // sub-expression.
29643005 } else if (auto tue = dyn_cast<TupleElementExpr>(e)) {
2965- if (findStorageReferenceExprForBorrow (tue->getBase ()))
2966- return tue;
3006+ if (auto result = findStorageReferenceExprForBorrow (tue->getBase ()))
3007+ return result.withTransitiveRoot (tue);
3008+
29673009 } else if (auto fve = dyn_cast<ForceValueExpr>(e)) {
2968- if (findStorageReferenceExprForBorrow (fve->getSubExpr ()))
2969- return fve;
3010+ if (auto result = findStorageReferenceExprForBorrow (fve->getSubExpr ()))
3011+ return result.withTransitiveRoot (fve);
3012+
29703013 } else if (auto boe = dyn_cast<BindOptionalExpr>(e)) {
2971- if (findStorageReferenceExprForBorrow (boe->getSubExpr ()))
2972- return boe;
3014+ if (auto result = findStorageReferenceExprForBorrow (boe->getSubExpr ()))
3015+ return result.withTransitiveRoot (boe);
3016+
29733017 } else if (auto oe = dyn_cast<OpenExistentialExpr>(e)) {
2974- if (findStorageReferenceExprForBorrow (oe->getExistentialValue ()) &&
2975- findStorageReferenceExprForBorrow (oe->getSubExpr ()))
2976- return oe;
3018+ if (findStorageReferenceExprForBorrow (oe->getExistentialValue ()))
3019+ if (auto result = findStorageReferenceExprForBorrow (oe->getSubExpr ()))
3020+ return result.withTransitiveRoot (oe);
3021+
29773022 } else if (auto bie = dyn_cast<DotSyntaxBaseIgnoredExpr>(e)) {
2978- if (findStorageReferenceExprForBorrow (bie->getRHS ()))
2979- return bie;
3023+ if (auto result = findStorageReferenceExprForBorrow (bie->getRHS ()))
3024+ return result.withTransitiveRoot (bie);
3025+
29803026 } else if (auto te = dyn_cast<AnyTryExpr>(e)) {
2981- if (findStorageReferenceExprForBorrow (te->getSubExpr ()))
2982- return te;
3027+ if (auto result = findStorageReferenceExprForBorrow (te->getSubExpr ()))
3028+ return result.withTransitiveRoot (te);
3029+
29833030 } else if (auto ioe = dyn_cast<InOutExpr>(e)) {
29843031 return ioe;
29853032 }
29863033
2987- return nullptr ;
3034+ return StorageRefResult () ;
29883035}
29893036
2990- Expr *ArgumentSource::findStorageReferenceExprForMoveOnlyBorrow (
2991- SILGenFunction &SGF) && {
3037+ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly (
3038+ SILGenFunction &SGF, StorageReferenceOperationKind kind ) && {
29923039 if (!isExpr ())
29933040 return nullptr ;
29943041
29953042 auto argExpr = asKnownExpr ();
2996- auto *li = dyn_cast<LoadExpr>(argExpr);
2997- if (!li)
3043+
3044+ // If there's a load around the outer part of this arg expr, look past it.
3045+ bool sawLoad = false ;
3046+ if (auto *li = dyn_cast<LoadExpr>(argExpr)) {
3047+ argExpr = li->getSubExpr ();
3048+ sawLoad = true ;
3049+ }
3050+
3051+ // If we're consuming instead, then the load _must_ have been there.
3052+ if (kind == StorageReferenceOperationKind::Consume && !sawLoad)
29983053 return nullptr ;
29993054
3000- auto *lvExpr = ::findStorageReferenceExprForBorrow (li-> getSubExpr () );
3055+ auto result = ::findStorageReferenceExprForBorrow (argExpr );
30013056
3002- // Claim the value of this argument if we found a storage reference that has a
3003- // move only base.
3004- if (lvExpr) {
3005- // We want to perform a borrow if our initial type is a pure move only /or/
3006- // if after looking through multiple copyable member ref expr, we get to a
3007- // move only member ref expr or a move only decl ref expr.
3008- //
3009- // NOTE: We purposely do not look through load implying that if we have
3010- // copyable types extracted from a move only class, we will not borrow.
3011- auto *iterExpr = lvExpr;
3012- while (true ) {
3013- SILType ty = SGF.getLoweredType (
3014- iterExpr->getType ()->getWithoutSpecifierType ()->getCanonicalType ());
3015- if (ty.isPureMoveOnly ())
3016- break ;
3057+ if (!result)
3058+ return nullptr ;
30173059
3018- if (auto *mre = dyn_cast<MemberRefExpr>(iterExpr)) {
3019- iterExpr = mre->getBase ();
3020- continue ;
3021- }
3060+ // We want to perform a borrow/consume if the first piece of storage being
3061+ // referenced is a move-only type.
3062+
3063+ VarDecl *storage = nullptr ;
3064+ Type type;
3065+ if (auto dre = dyn_cast<DeclRefExpr>(result.getStorageRef ())) {
3066+ storage = dyn_cast<VarDecl>(dre->getDecl ());
3067+ type = dre->getType ();
3068+ } else if (auto mre = dyn_cast<MemberRefExpr>(result.getStorageRef ())) {
3069+ storage = dyn_cast<VarDecl>(mre->getDecl ().getDecl ());
3070+ type = mre->getType ();
3071+ }
30223072
3073+ if (!storage)
30233074 return nullptr ;
3024- }
3075+ assert (type);
30253076
3026- (void )std::move (*this ).asKnownExpr ();
3027- }
3077+ SILType ty =
3078+ SGF.getLoweredType (type->getWithoutSpecifierType ()->getCanonicalType ());
3079+ if (!ty.isPureMoveOnly ())
3080+ return nullptr ;
30283081
3029- return lvExpr;
3082+ // It makes sense to borrow any kind of storage we refer to at this stage,
3083+ // but SILGenLValue does not currently handle some kinds of references well.
3084+ //
3085+ // When rejecting to do the LValue-style borrow here, it'll end up going thru
3086+ // the RValue-style emission, after which the extra copy will get eliminated.
3087+ //
3088+ // If we did not see a LoadExpr around the argument expression, then only
3089+ // do the borrow if the storage is non-local.
3090+ // FIXME: I don't have a principled reason for why this matters and hope that
3091+ // we can fix the AST we're working with.
3092+ if (!sawLoad && storage->getDeclContext ()->isLocalContext ())
3093+ return nullptr ;
3094+
3095+ // Claim the value of this argument since we found a storage reference that
3096+ // has a move only base.
3097+ (void )std::move (*this ).asKnownExpr ();
3098+
3099+ return result.getTransitiveRoot ();
30303100}
30313101
30323102Expr *
@@ -3054,7 +3124,8 @@ ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
30543124 if (!borrowExpr)
30553125 return nullptr ;
30563126
3057- auto *lvExpr = ::findStorageReferenceExprForBorrow (borrowExpr->getSubExpr ());
3127+ Expr *lvExpr = ::findStorageReferenceExprForBorrow (borrowExpr->getSubExpr ())
3128+ .getTransitiveRoot ();
30583129
30593130 // Claim the value of this argument.
30603131 if (lvExpr) {
@@ -3068,7 +3139,8 @@ Expr *ArgumentSource::findStorageReferenceExprForBorrow() && {
30683139 if (!isExpr ()) return nullptr ;
30693140
30703141 auto argExpr = asKnownExpr ();
3071- auto lvExpr = ::findStorageReferenceExprForBorrow (argExpr);
3142+ auto *lvExpr =
3143+ ::findStorageReferenceExprForBorrow (argExpr).getTransitiveRoot();
30723144
30733145 // Claim the value of this argument if we found a storage reference.
30743146 if (lvExpr) {
@@ -3318,8 +3390,8 @@ class ArgEmitter {
33183390 }
33193391
33203392 if (IsYield) {
3321- if (auto lvExpr = findStorageReferenceExprForBorrow (e)) {
3322- emitExpandedBorrowed (lvExpr , origParamType);
3393+ if (auto result = findStorageReferenceExprForBorrow (e)) {
3394+ emitExpandedBorrowed (result. getTransitiveRoot () , origParamType);
33233395 return ;
33243396 }
33253397 }
@@ -3431,7 +3503,8 @@ class ArgEmitter {
34313503 assert (paramsSlice.size () == 1 );
34323504
34333505 // Try to find an expression we can emit as a borrowed l-value.
3434- auto lvExpr = std::move (arg).findStorageReferenceExprForMoveOnlyBorrow (SGF);
3506+ auto lvExpr = std::move (arg).findStorageReferenceExprForMoveOnly (
3507+ SGF, ArgumentSource::StorageReferenceOperationKind::Borrow);
34353508 if (!lvExpr)
34363509 return false ;
34373510
@@ -3503,8 +3576,9 @@ class ArgEmitter {
35033576 ClaimedParamsRef paramsSlice) {
35043577 assert (paramsSlice.size () == 1 );
35053578
3506- // Try to find an expression we can emit as a borrowed l-value.
3507- auto lvExpr = std::move (arg).findStorageReferenceExprForMoveOnlyBorrow (SGF);
3579+ // Try to find an expression we can emit as a consumed l-value.
3580+ auto lvExpr = std::move (arg).findStorageReferenceExprForMoveOnly (
3581+ SGF, ArgumentSource::StorageReferenceOperationKind::Consume);
35083582 if (!lvExpr)
35093583 return false ;
35103584
0 commit comments