Skip to content

Commit 2f519f4

Browse files
committed
SILGen: Emit borrowing switch subjects under a formal access.
Ensure that dependent accesses are properly nested when a subject isn't directly a borrowed parameter binding.
1 parent 067f1d3 commit 2f519f4

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
@@ -3193,13 +3193,8 @@ static StorageRefResult findStorageReferenceExprForBorrow(Expr *e) {
31933193
return StorageRefResult();
31943194
}
31953195

3196-
Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
3197-
SILGenFunction &SGF, StorageReferenceOperationKind kind) && {
3198-
if (!isExpr())
3199-
return nullptr;
3200-
3201-
auto argExpr = asKnownExpr();
3202-
3196+
Expr *SILGenFunction::findStorageReferenceExprForMoveOnly(Expr *argExpr,
3197+
StorageReferenceOperationKind kind) {
32033198
// If there's a load around the outer part of this arg expr, look past it.
32043199
bool sawLoad = false;
32053200
if (auto *li = dyn_cast<LoadExpr>(argExpr)) {
@@ -3267,7 +3262,7 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
32673262
assert(type);
32683263

32693264
SILType ty =
3270-
SGF.getLoweredType(type->getWithoutSpecifierType()->getCanonicalType());
3265+
getLoweredType(type->getWithoutSpecifierType()->getCanonicalType());
32713266
bool isMoveOnly = ty.isMoveOnly(/*orWrapped=*/false);
32723267
if (auto *pd = dyn_cast<ParamDecl>(storage)) {
32733268
isMoveOnly |= pd->getSpecifier() == ParamSpecifier::Borrowing;
@@ -3276,10 +3271,6 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
32763271
if (!isMoveOnly)
32773272
return nullptr;
32783273

3279-
// Claim the value of this argument since we found a storage reference that
3280-
// has a move only base.
3281-
(void)std::move(*this).asKnownExpr();
3282-
32833274
// If we saw a subscript expr and the base of the subscript expr passed our
32843275
// tests above, we can emit the call to the subscript directly as a borrowed
32853276
// lvalue. Return the subscript expr here so that we emit it appropriately.
@@ -3289,11 +3280,22 @@ Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
32893280
return result.getTransitiveRoot();
32903281
}
32913282

3292-
Expr *
3293-
ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
3283+
Expr *ArgumentSource::findStorageReferenceExprForMoveOnly(
3284+
SILGenFunction &SGF, StorageReferenceOperationKind kind) && {
32943285
if (!isExpr())
32953286
return nullptr;
32963287

3288+
auto lvExpr = SGF.findStorageReferenceExprForMoveOnly(asKnownExpr(), kind);
3289+
if (lvExpr) {
3290+
// Claim the value of this argument since we found a storage reference that
3291+
// has a move only base.
3292+
(void)std::move(*this).asKnownExpr();
3293+
}
3294+
return lvExpr;
3295+
}
3296+
3297+
Expr *
3298+
SILGenFunction::findStorageReferenceExprForBorrowExpr(Expr *argExpr) {
32973299
// We support two patterns:
32983300
//
32993301
// (load_expr (borrow_expr))
@@ -3302,8 +3304,6 @@ ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
33023304
//
33033305
// The first happens if a borrow is used on a non-self argument. The second
33043306
// happens if we pass self as a borrow.
3305-
auto *argExpr = asKnownExpr();
3306-
33073307
if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr))
33083308
argExpr = parenExpr->getSubExpr();
33093309

@@ -3314,14 +3314,21 @@ ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
33143314
if (!borrowExpr)
33153315
return nullptr;
33163316

3317-
Expr *lvExpr = ::findStorageReferenceExprForBorrow(borrowExpr->getSubExpr())
3317+
return ::findStorageReferenceExprForBorrow(borrowExpr->getSubExpr())
33183318
.getTransitiveRoot();
3319+
}
33193320

3321+
Expr *
3322+
ArgumentSource::findStorageReferenceExprForBorrowExpr(SILGenFunction &SGF) && {
3323+
if (!isExpr())
3324+
return nullptr;
3325+
3326+
auto lvExpr = SGF.findStorageReferenceExprForBorrowExpr(asKnownExpr());
33203327
// Claim the value of this argument.
33213328
if (lvExpr) {
33223329
(void)std::move(*this).asKnownExpr();
33233330
}
3324-
3331+
33253332
return lvExpr;
33263333
}
33273334

@@ -3683,7 +3690,7 @@ class ArgEmitter {
36833690

36843691
// Try to find an expression we can emit as a borrowed l-value.
36853692
auto lvExpr = std::move(arg).findStorageReferenceExprForMoveOnly(
3686-
SGF, ArgumentSource::StorageReferenceOperationKind::Borrow);
3693+
SGF, StorageReferenceOperationKind::Borrow);
36873694
if (!lvExpr)
36883695
return false;
36893696

@@ -3757,7 +3764,7 @@ class ArgEmitter {
37573764

37583765
// Try to find an expression we can emit as a consumed l-value.
37593766
auto lvExpr = std::move(arg).findStorageReferenceExprForMoveOnly(
3760-
SGF, ArgumentSource::StorageReferenceOperationKind::Consume);
3767+
SGF, StorageReferenceOperationKind::Consume);
37613768
if (!lvExpr)
37623769
return false;
37633770

@@ -3788,7 +3795,7 @@ class ArgEmitter {
37883795
void
37893796
emitDirect(ArgumentSource &&arg, SILType loweredSubstArgType,
37903797
AbstractionPattern origParamType, SILParameterInfo param,
3791-
llvm::Optional<AnyFunctionType::Param> origParam = llvm::None) {
3798+
llvm::Optional<AnyFunctionType::Param> origParam = llvm::None) {
37923799
ManagedValue value;
37933800
auto loc = arg.getLocation();
37943801

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>
@@ -1511,6 +1517,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
15111517
/// Emit the given expression as an r-value.
15121518
RValue emitRValue(Expr *E, SGFContext C = SGFContext());
15131519

1520+
/// Given an expression, find the subexpression that can be emitted as a borrow formal access, if
1521+
/// any.
1522+
Expr *findStorageReferenceExprForMoveOnly(Expr *argExpr,
1523+
StorageReferenceOperationKind kind);
1524+
Expr *findStorageReferenceExprForBorrowExpr(Expr *argExpr);
1525+
15141526
/// Emit the given expression as a +1 r-value.
15151527
///
15161528
/// *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)