Skip to content

Commit 2092240

Browse files
authored
Merge pull request #71406 from jckarter/borrowing-switch-5
SILGen: Emit borrowing switch subjects under a formal access.
2 parents f98b211 + 2f519f4 commit 2092240

File tree

7 files changed

+354
-64
lines changed

7 files changed

+354
-64
lines changed

lib/SILGen/ArgumentSource.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,6 @@ class ArgumentSource {
222222
return Storage.get<LValueStorage>(StoredKind).Loc;
223223
}
224224

225-
/// The kind of operation under which we are querying a storage reference.
226-
enum class StorageReferenceOperationKind {
227-
Borrow,
228-
Consume
229-
};
230-
231225
Expr *findStorageReferenceExprForBorrow() &&;
232226
Expr *findStorageReferenceExprForMoveOnly(SILGenFunction &SGF,
233227
StorageReferenceOperationKind refKind) &&;

lib/SILGen/SILGenApply.cpp

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3196,13 +3196,8 @@ static StorageRefResult findStorageReferenceExprForBorrow(Expr *e) {
31963196
return StorageRefResult();
31973197
}
31983198

3199-
Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
3200-
SILGenFunction &SGF, StorageReferenceOperationKind kind) && {
3201-
if (!isExpr())
3202-
return nullptr;
3203-
3204-
auto argExpr = asKnownExpr();
3205-
3199+
Expr *SILGenFunction::findStorageReferenceExprForMoveOnly(Expr *argExpr,
3200+
StorageReferenceOperationKind kind) {
32063201
// If there's a load around the outer part of this arg expr, look past it.
32073202
bool sawLoad = false;
32083203
if (auto *li = dyn_cast<LoadExpr>(argExpr)) {
@@ -3270,7 +3265,7 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
32703265
assert(type);
32713266

32723267
SILType ty =
3273-
SGF.getLoweredType(type->getWithoutSpecifierType()->getCanonicalType());
3268+
getLoweredType(type->getWithoutSpecifierType()->getCanonicalType());
32743269
bool isMoveOnly = ty.isMoveOnly(/*orWrapped=*/false);
32753270
if (auto *pd = dyn_cast<ParamDecl>(storage)) {
32763271
isMoveOnly |= pd->getSpecifier() == ParamSpecifier::Borrowing;
@@ -3279,10 +3274,6 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
32793274
if (!isMoveOnly)
32803275
return nullptr;
32813276

3282-
// Claim the value of this argument since we found a storage reference that
3283-
// has a move only base.
3284-
(void)std::move(*this).asKnownExpr();
3285-
32863277
// If we saw a subscript expr and the base of the subscript expr passed our
32873278
// tests above, we can emit the call to the subscript directly as a borrowed
32883279
// lvalue. Return the subscript expr here so that we emit it appropriately.
@@ -3292,11 +3283,22 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
32923283
return result.getTransitiveRoot();
32933284
}
32943285

3295-
Expr *
3296-
ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
3286+
Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
3287+
SILGenFunction &SGF, StorageReferenceOperationKind kind) && {
32973288
if (!isExpr())
32983289
return nullptr;
32993290

3291+
auto lvExpr = SGF.findStorageReferenceExprForMoveOnly(asKnownExpr(), kind);
3292+
if (lvExpr) {
3293+
// Claim the value of this argument since we found a storage reference that
3294+
// has a move only base.
3295+
(void)std::move(*this).asKnownExpr();
3296+
}
3297+
return lvExpr;
3298+
}
3299+
3300+
Expr *
3301+
SILGenFunction::findStorageReferenceExprForBorrowExpr(Expr *argExpr) {
33003302
// We support two patterns:
33013303
//
33023304
// (load_expr (borrow_expr))
@@ -3305,8 +3307,6 @@ ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
33053307
//
33063308
// The first happens if a borrow is used on a non-self argument. The second
33073309
// happens if we pass self as a borrow.
3308-
auto *argExpr = asKnownExpr();
3309-
33103310
if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr))
33113311
argExpr = parenExpr->getSubExpr();
33123312

@@ -3317,14 +3317,21 @@ ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
33173317
if (!borrowExpr)
33183318
return nullptr;
33193319

3320-
Expr *lvExpr = ::findStorageReferenceExprForBorrow(borrowExpr->getSubExpr())
3320+
return ::findStorageReferenceExprForBorrow(borrowExpr->getSubExpr())
33213321
.getTransitiveRoot();
3322+
}
33223323

3324+
Expr *
3325+
ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
3326+
if (!isExpr())
3327+
return nullptr;
3328+
3329+
auto lvExpr = SGF.findStorageReferenceExprForBorrowExpr(asKnownExpr());
33233330
// Claim the value of this argument.
33243331
if (lvExpr) {
33253332
(void)std::move(*this).asKnownExpr();
33263333
}
3327-
3334+
33283335
return lvExpr;
33293336
}
33303337

@@ -3686,7 +3693,7 @@ class ArgEmitter {
36863693

36873694
// Try to find an expression we can emit as a borrowed l-value.
36883695
auto lvExpr = std::move(arg).findStorageReferenceExprForMoveOnly(
3689-
SGF, ArgumentSource::StorageReferenceOperationKind::Borrow);
3696+
SGF, StorageReferenceOperationKind::Borrow);
36903697
if (!lvExpr)
36913698
return false;
36923699

@@ -3760,7 +3767,7 @@ class ArgEmitter {
37603767

37613768
// Try to find an expression we can emit as a consumed l-value.
37623769
auto lvExpr = std::move(arg).findStorageReferenceExprForMoveOnly(
3763-
SGF, ArgumentSource::StorageReferenceOperationKind::Consume);
3770+
SGF, StorageReferenceOperationKind::Consume);
37643771
if (!lvExpr)
37653772
return false;
37663773

@@ -3791,7 +3798,7 @@ class ArgEmitter {
37913798
void
37923799
emitDirect(ArgumentSource &&arg, SILType loweredSubstArgType,
37933800
AbstractionPattern origParamType, SILParameterInfo param,
3794-
llvm::Optional<AnyFunctionType::Param> origParam = llvm::None) {
3801+
llvm::Optional<AnyFunctionType::Param> origParam = llvm::None) {
37953802
ManagedValue value;
37963803
auto loc = arg.getLocation();
37973804

lib/SILGen/SILGenFunction.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ struct MaterializedLValue {
272272
callbackStorage(callbackStorage) {}
273273
};
274274

275+
/// The kind of operation under which we are querying a storage reference.
276+
enum class StorageReferenceOperationKind {
277+
Borrow,
278+
Consume
279+
};
280+
275281
/// SILGenFunction - an ASTVisitor for producing SIL from function bodies.
276282
class LLVM_LIBRARY_VISIBILITY SILGenFunction
277283
: public ASTVisitor<SILGenFunction>
@@ -1518,6 +1524,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
15181524
/// Emit the given expression as an r-value.
15191525
RValue emitRValue(Expr *E, SGFContext C = SGFContext());
15201526

1527+
/// Given an expression, find the subexpression that can be emitted as a borrow formal access, if
1528+
/// any.
1529+
Expr *findStorageReferenceExprForMoveOnly(Expr *argExpr,
1530+
StorageReferenceOperationKind kind);
1531+
Expr *findStorageReferenceExprForBorrowExpr(Expr *argExpr);
1532+
15211533
/// Emit the given expression as a +1 r-value.
15221534
///
15231535
/// *NOTE* This creates the +1 r-value and then pushes that +1 r-value through

lib/SILGen/SILGenPattern.cpp

Lines changed: 113 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,6 +3178,9 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
31783178
llvm::dbgs() << '\n');
31793179

31803180
auto subjectTy = S->getSubjectExpr()->getType();
3181+
auto subjectLoweredTy = getLoweredType(subjectTy);
3182+
auto subjectLoweredAddress =
3183+
silConv.useLoweredAddresses() && subjectLoweredTy.isAddressOnly(F);
31813184

31823185
// If the subject expression is uninhabited, we're already dead.
31833186
// Emit an unreachable in place of the switch statement.
@@ -3254,27 +3257,88 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
32543257
emission.emitAddressOnlyAllocations();
32553258

32563259
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();
32593276
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;
32603289

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+
}
32643296

32653297
PatternMatchContext switchContext = { emission };
32663298
SwitchStack.push_back(&switchContext);
32673299

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+
32783342
// Inline constructor for subject.
32793343
auto subject = ([&]() -> ConsumableManagedValue {
32803344
if (subjectMV.getType().isMoveOnly()) {
@@ -3292,12 +3356,15 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
32923356

32933357
case ValueOwnership::Shared:
32943358
emission.setNoncopyableBorrowingOwnership();
3359+
if (!subjectMV.isPlusZero()) {
3360+
subjectMV = subjectMV.borrow(*this, S);
3361+
}
32953362
if (subjectMV.getType().isAddress() &&
32963363
subjectMV.getType().isLoadable(F)) {
32973364
// 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);
33013368
}
33023369
return {subjectMV, CastConsumptionKind::BorrowAlways};
33033370

@@ -3385,6 +3452,20 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
33853452
return {subjectMV.copy(*this, S), CastConsumptionKind::TakeAlways};
33863453
}());
33873454

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+
33883469
// If we need to diagnose an unexpected enum case or unexpected enum case
33893470
// value, we need access to a value metatype for the subject. Emit this state
33903471
// now before we emit the actual switch to ensure that the subject has not
@@ -3446,7 +3527,14 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
34463527
}
34473528

34483529
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+
}
34503538

34513539
// Then emit the case blocks shared by multiple pattern cases.
34523540
emission.emitSharedCaseBlocks(
@@ -3463,6 +3551,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
34633551
} else {
34643552
B.emitBlock(contBB);
34653553
}
3554+
3555+
// End the formal access to the subject now (if there was one).
3556+
if (subjectUndergoesFormalAccess) {
3557+
switchFormalAccess.reset();
3558+
switchScope.pop();
3559+
}
34663560

34673561
// Now that we have emitted everything, see if our unexpected enum case info
34683562
// metatypes were actually used. If not, delete them.

0 commit comments

Comments
 (0)