Skip to content

Commit 96c87db

Browse files
committed
Move-only-check the yielded result from read coroutines when they're noncopyable.
Mark the result of starting a read coroutine to be checked by the move-only checker, and then update the pattern matching in the move checker itself so that it recognizes code patterns involving yielding from and receiving yields from read coroutines. Teach move only diagnostics to get the property name for an access through a read coroutine from the referenced declaration.
1 parent 1cb92c0 commit 96c87db

File tree

6 files changed

+274
-33
lines changed

6 files changed

+274
-33
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4822,17 +4822,28 @@ namespace {
48224822
/// Cleanup to end a coroutine application.
48234823
class EndCoroutineApply : public Cleanup {
48244824
SILValue ApplyToken;
4825+
std::vector<BeginBorrowInst *> BorrowedMoveOnlyValues;
48254826
public:
48264827
EndCoroutineApply(SILValue applyToken) : ApplyToken(applyToken) {}
48274828

4829+
void setBorrowedMoveOnlyValues(ArrayRef<BeginBorrowInst *> values) {
4830+
BorrowedMoveOnlyValues.insert(BorrowedMoveOnlyValues.end(),
4831+
values.begin(), values.end());
4832+
}
4833+
48284834
void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override {
4835+
for (auto i = BorrowedMoveOnlyValues.rbegin(), e = BorrowedMoveOnlyValues.rend();
4836+
i != e; ++i) {
4837+
SGF.B.createEndBorrow(l, *i);
4838+
SGF.B.createDestroyValue(l, (*i)->getOperand());
4839+
}
48294840
if (forUnwind) {
48304841
SGF.B.createAbortApply(l, ApplyToken);
48314842
} else {
48324843
SGF.B.createEndApply(l, ApplyToken);
48334844
}
48344845
}
4835-
4846+
48364847
void dump(SILGenFunction &SGF) const override {
48374848
#ifndef NDEBUG
48384849
llvm::errs() << "EndCoroutineApply "
@@ -4897,23 +4908,51 @@ SILGenFunction::emitBeginApply(SILLocation loc, ManagedValue fn,
48974908
auto yieldInfos = substFnType->getYields();
48984909
assert(yieldValues.size() == yieldInfos.size());
48994910
bool useLoweredAddresses = silConv.useLoweredAddresses();
4911+
SmallVector<BeginBorrowInst *, 2> borrowedMoveOnlyValues;
49004912
for (auto i : indices(yieldValues)) {
49014913
auto value = yieldValues[i];
49024914
auto info = yieldInfos[i];
49034915
if (info.isIndirectInOut()) {
49044916
yields.push_back(ManagedValue::forLValue(value));
49054917
} else if (info.isConsumed()) {
4918+
if (value->getType().isMoveOnly()) {
4919+
value = B.createMarkUnresolvedNonCopyableValueInst(loc, value,
4920+
MarkUnresolvedNonCopyableValueInst::CheckKind::ConsumableAndAssignable);
4921+
}
49064922
!useLoweredAddresses && value->getType().isTrivial(getFunction())
49074923
? yields.push_back(ManagedValue::forRValueWithoutOwnership(value))
49084924
: yields.push_back(emitManagedRValueWithCleanup(value));
49094925
} else if (info.isGuaranteed()) {
4910-
!useLoweredAddresses && value->getType().isTrivial(getFunction())
4911-
? yields.push_back(ManagedValue::forRValueWithoutOwnership(value))
4912-
: yields.push_back(ManagedValue::forBorrowedRValue(value));
4926+
if (value->getType().isMoveOnly()) {
4927+
if (!value->getType().isAddress()) {
4928+
// The move checker uses the lifetime of the "copy" for borrow checking.
4929+
value = B.createCopyValue(loc, value);
4930+
value = B.createMarkUnresolvedNonCopyableValueInst(loc, value,
4931+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
4932+
auto borrow = B.createBeginBorrow(loc, value);
4933+
yields.push_back(ManagedValue::forBorrowedRValue(borrow));
4934+
borrowedMoveOnlyValues.push_back(borrow);
4935+
} else {
4936+
value = B.createMarkUnresolvedNonCopyableValueInst(loc, value,
4937+
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
4938+
yields.push_back(ManagedValue::forRValueWithoutOwnership(value));
4939+
}
4940+
} else {
4941+
!useLoweredAddresses && value->getType().isTrivial(getFunction())
4942+
? yields.push_back(ManagedValue::forRValueWithoutOwnership(value))
4943+
: yields.push_back(ManagedValue::forBorrowedRValue(value));
4944+
}
49134945
} else {
4946+
assert(!value->getType().isMoveOnly()
4947+
&& "move-only types shouldn't be trivial");
49144948
yields.push_back(ManagedValue::forRValueWithoutOwnership(value));
49154949
}
49164950
}
4951+
4952+
if (!borrowedMoveOnlyValues.empty()) {
4953+
auto &endApply = static_cast<EndCoroutineApply &>(Cleanups.getCleanup(endApplyHandle));
4954+
endApply.setBorrowedMoveOnlyValues(borrowedMoveOnlyValues);
4955+
}
49174956

49184957
return endApplyHandle;
49194958
}

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,9 @@ void UseState::initializeLiveness(
950950
reinitInstAndValue.second);
951951
}
952952
}
953+
954+
// FIXME: Whether the initial use is an initialization ought to be entirely
955+
// derivable from the CheckKind of the mark instruction.
953956

954957
// Then check if our markedValue is from an argument that is in,
955958
// in_guaranteed, inout, or inout_aliasable, consider the marked address to be
@@ -1048,6 +1051,12 @@ void UseState::initializeLiveness(
10481051
recordInitUse(address, address, liveness.getTopLevelSpan());
10491052
liveness.initializeDef(address, liveness.getTopLevelSpan());
10501053
}
1054+
1055+
if (auto *bai = dyn_cast_or_null<BeginApplyInst>(
1056+
stripAccessMarkers(address->getOperand())->getDefiningInstruction())) {
1057+
recordInitUse(address, address, liveness.getTopLevelSpan());
1058+
liveness.initializeDef(address, liveness.getTopLevelSpan());
1059+
}
10511060

10521061
// Now that we have finished initialization of defs, change our multi-maps
10531062
// from their array form to their map form.

lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,21 @@ static void getVariableNameForValue(SILValue value2,
113113
break;
114114
}
115115

116+
if (auto bai = dyn_cast_or_null<BeginApplyInst>(searchValue->getDefiningInstruction())) {
117+
// Use the name of the property being accessed if we can get to it.
118+
if (isa<FunctionRefBaseInst>(bai->getCallee())
119+
|| isa<MethodInst>(bai->getCallee())) {
120+
variableNamePath.push_back(bai->getCallee()->getDefiningInstruction());
121+
// Try to name the base of the property if this is a method.
122+
if (bai->getSubstCalleeType()->hasSelfParam()) {
123+
searchValue = bai->getSelfArgument();
124+
continue;
125+
} else {
126+
break;
127+
}
128+
}
129+
}
130+
116131
// If we do not do an exact match, see if we can find a debug_var inst. If
117132
// we do, we always break since we have a root value.
118133
if (auto *use = getAnyDebugUse(searchValue)) {
@@ -132,12 +147,25 @@ static void getVariableNameForValue(SILValue value2,
132147
searchValue = cast<SingleValueInstruction>(searchValue)->getOperand(0);
133148
continue;
134149
}
135-
150+
136151
// If we do not pattern match successfully, just set resulting string to
137152
// unknown and return early.
138153
resultingString += "unknown";
139154
return;
140155
}
156+
157+
auto nameFromDecl = [&](Decl *d) -> StringRef {
158+
if (d) {
159+
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
160+
return accessor->getStorage()->getBaseName().userFacingName();
161+
}
162+
if (auto vd = dyn_cast<ValueDecl>(d)) {
163+
return vd->getBaseName().userFacingName();
164+
}
165+
}
166+
167+
return "<unknown decl>";
168+
};
141169

142170
// Walk backwards, constructing our string.
143171
while (true) {
@@ -148,6 +176,16 @@ static void getVariableNameForValue(SILValue value2,
148176
resultingString += i.getName();
149177
} else if (auto i = VarDeclCarryingInst(inst)) {
150178
resultingString += i.getName();
179+
} else if (auto f = dyn_cast<FunctionRefBaseInst>(inst)) {
180+
if (auto dc = f->getInitiallyReferencedFunction()->getDeclContext()) {
181+
resultingString += nameFromDecl(dc->getAsDecl());
182+
} else {
183+
resultingString += "<unknown decl>";
184+
}
185+
} else if (auto m = dyn_cast<MethodInst>(inst)) {
186+
resultingString += nameFromDecl(m->getMember().getDecl());
187+
} else {
188+
resultingString += "<unknown decl>";
151189
}
152190
} else {
153191
auto value = next.get<SILValue>();

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,11 @@ void MoveOnlyObjectCheckerPImpl::check(DominanceInfo *domTree,
481481
i = copyToMoveOnly;
482482
}
483483

484+
// TODO: Instead of pattern matching specific code generation patterns,
485+
// we should be able to eliminate any `copy_value` whose canonical
486+
// lifetime fits within the borrow scope of the borrowed value being
487+
// copied from.
488+
484489
// Handle:
485490
//
486491
// bb0(%0 : @guaranteed $Type):
@@ -521,6 +526,24 @@ void MoveOnlyObjectCheckerPImpl::check(DominanceInfo *domTree,
521526
}
522527
}
523528
}
529+
530+
// Handle:
531+
// (%yield, ..., %handle) = begin_apply
532+
// %copy = copy_value %yield
533+
// %mark = mark_unresolved_noncopyable_value [no_consume_or_assign] %copy
534+
if (auto bai = dyn_cast_or_null<BeginApplyInst>(i->getOperand(0)->getDefiningInstruction())) {
535+
if (i->getOperand(0)->getOwnershipKind() == OwnershipKind::Guaranteed) {
536+
for (auto *use : markedInst->getConsumingUses()) {
537+
destroys.push_back(cast<DestroyValueInst>(use->getUser()));
538+
}
539+
while (!destroys.empty())
540+
destroys.pop_back_val()->eraseFromParent();
541+
markedInst->replaceAllUsesWith(i->getOperand(0));
542+
markedInst->eraseFromParent();
543+
cvi->eraseFromParent();
544+
continue;
545+
}
546+
}
524547
}
525548
}
526549
}

0 commit comments

Comments
 (0)