Skip to content

Reland Allow normal function results of @yield_once coroutines #71645

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions docs/SIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6100,6 +6100,14 @@ executing the ``begin_apply``) were being "called" by the ``yield``:
or move the value from that position before ending or aborting the
coroutine.

A coroutine optionally may produce normal results. These do not have
``@yields`` annotation in the result type tuple.
::
(%float, %token) = begin_apply %0() : $@yield_once () -> (@yields Float, Int)

Normal results of a coroutine are produced by the corresponding ``end_apply``
instruction.

A ``begin_apply`` must be uniquely either ended or aborted before
exiting the function or looping to an earlier portion of the function.

Expand Down Expand Up @@ -6129,9 +6137,9 @@ end_apply
`````````
::

sil-instruction ::= 'end_apply' sil-value
sil-instruction ::= 'end_apply' sil-value 'as' sil-type

end_apply %token
end_apply %token as $()

Ends the given coroutine activation, which is currently suspended at
a ``yield`` instruction. Transfers control to the coroutine and takes
Expand All @@ -6141,8 +6149,8 @@ when the coroutine reaches a ``return`` instruction.
The operand must always be the token result of a ``begin_apply``
instruction, which is why it need not specify a type.

``end_apply`` currently has no instruction results. If coroutines were
allowed to have normal results, they would be producted by ``end_apply``.
The result of ``end_apply`` is the normal result of the coroutine function (the
operand of the ``return`` instruction)."

When throwing coroutines are supported, there will need to be a
``try_end_apply`` instruction.
Expand Down
41 changes: 22 additions & 19 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4789,24 +4789,27 @@ class SILFunctionType final
using Representation = SILExtInfoBuilder::Representation;

private:
unsigned NumParameters;
unsigned NumParameters = 0;

// These are *normal* results if this is not a coroutine and *yield* results
// otherwise.
unsigned NumAnyResults; // Not including the ErrorResult.
unsigned NumAnyIndirectFormalResults; // Subset of NumAnyResults.
unsigned NumPackResults; // Subset of NumAnyIndirectFormalResults.
// These are *normal* results
unsigned NumAnyResults = 0; // Not including the ErrorResult.
unsigned NumAnyIndirectFormalResults = 0; // Subset of NumAnyResults.
unsigned NumPackResults = 0; // Subset of NumAnyIndirectFormalResults.
// These are *yield* results
unsigned NumAnyYieldResults = 0; // Not including the ErrorResult.
unsigned NumAnyIndirectFormalYieldResults = 0; // Subset of NumAnyYieldResults.
unsigned NumPackYieldResults = 0; // Subset of NumAnyIndirectFormalYieldResults.

// [NOTE: SILFunctionType-layout]
// The layout of a SILFunctionType in memory is:
// SILFunctionType
// SILParameterInfo[NumParameters]
// SILResultInfo[isCoroutine() ? 0 : NumAnyResults]
// SILResultInfo[NumAnyResults]
// SILResultInfo? // if hasErrorResult()
// SILYieldInfo[isCoroutine() ? NumAnyResults : 0]
// SILYieldInfo[NumAnyYieldResults]
// SubstitutionMap[HasPatternSubs + HasInvocationSubs]
// CanType? // if !isCoro && NumAnyResults > 1, formal result cache
// CanType? // if !isCoro && NumAnyResults > 1, all result cache
// CanType? // if NumAnyResults > 1, formal result cache
// CanType? // if NumAnyResults > 1, all result cache

CanGenericSignature InvocationGenericSig;
ProtocolConformanceRef WitnessMethodConformance;
Expand Down Expand Up @@ -4845,7 +4848,7 @@ class SILFunctionType final

/// Do we have slots for caches of the normal-result tuple type?
bool hasResultCache() const {
return NumAnyResults > 1 && !isCoroutine();
return NumAnyResults > 1;
}

CanType &getMutableFormalResultsCache() const {
Expand Down Expand Up @@ -4945,14 +4948,14 @@ class SILFunctionType final
ArrayRef<SILYieldInfo> getYields() const {
return const_cast<SILFunctionType *>(this)->getMutableYields();
}
unsigned getNumYields() const { return isCoroutine() ? NumAnyResults : 0; }
unsigned getNumYields() const { return NumAnyYieldResults; }

/// Return the array of all result information. This may contain inter-mingled
/// direct and indirect results.
ArrayRef<SILResultInfo> getResults() const {
return const_cast<SILFunctionType *>(this)->getMutableResults();
}
unsigned getNumResults() const { return isCoroutine() ? 0 : NumAnyResults; }
unsigned getNumResults() const { return NumAnyResults; }

ArrayRef<SILResultInfo> getResultsWithError() const {
return const_cast<SILFunctionType *>(this)->getMutableResultsWithError();
Expand Down Expand Up @@ -4989,17 +4992,17 @@ class SILFunctionType final
// indirect property, not the SIL indirect property, should be consulted to
// determine whether function reabstraction is necessary.
unsigned getNumIndirectFormalResults() const {
return isCoroutine() ? 0 : NumAnyIndirectFormalResults;
return NumAnyIndirectFormalResults;
}
/// Does this function have any formally indirect results?
bool hasIndirectFormalResults() const {
return getNumIndirectFormalResults() != 0;
}
unsigned getNumDirectFormalResults() const {
return isCoroutine() ? 0 : NumAnyResults - NumAnyIndirectFormalResults;
return NumAnyResults - NumAnyIndirectFormalResults;
}
unsigned getNumPackResults() const {
return isCoroutine() ? 0 : NumPackResults;
return NumPackResults;
}
bool hasIndirectErrorResult() const {
return hasErrorResult() && getErrorResult().isFormalIndirect();
Expand Down Expand Up @@ -5057,17 +5060,17 @@ class SILFunctionType final
TypeExpansionContext expansion);

unsigned getNumIndirectFormalYields() const {
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
return NumAnyIndirectFormalYieldResults;
}
/// Does this function have any formally indirect yields?
bool hasIndirectFormalYields() const {
return getNumIndirectFormalYields() != 0;
}
unsigned getNumDirectFormalYields() const {
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
return NumAnyYieldResults - NumAnyIndirectFormalYieldResults;
}
unsigned getNumPackYields() const {
return isCoroutine() ? NumPackResults : 0;
return NumPackYieldResults;
}

struct IndirectFormalYieldFilter {
Expand Down
6 changes: 3 additions & 3 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -589,11 +589,11 @@ class SILBuilder {
beginApply));
}

EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply) {
EndApplyInst *createEndApply(SILLocation loc, SILValue beginApply, SILType ResultType) {
return insert(new (getModule()) EndApplyInst(getSILDebugLocation(loc),
beginApply));
beginApply, ResultType));
}

BuiltinInst *createBuiltin(SILLocation Loc, Identifier Name, SILType ResultTy,
SubstitutionMap Subs,
ArrayRef<SILValue> Args) {
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/SILCloner.h
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,8 @@ SILCloner<ImplClass>::visitEndApplyInst(EndApplyInst *Inst) {
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
recordClonedInstruction(
Inst, getBuilder().createEndApply(getOpLocation(Inst->getLoc()),
getOpValue(Inst->getOperand())));
getOpValue(Inst->getOperand()),
getOpType(Inst->getType())));
}

template<typename ImplClass>
Expand Down
7 changes: 4 additions & 3 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3253,11 +3253,12 @@ class AbortApplyInst
/// normally.
class EndApplyInst
: public UnaryInstructionBase<SILInstructionKind::EndApplyInst,
NonValueInstruction> {
SingleValueInstruction> {
friend SILBuilder;

EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken)
: UnaryInstructionBase(debugLoc, beginApplyToken) {
EndApplyInst(SILDebugLocation debugLoc, SILValue beginApplyToken,
SILType Ty)
: UnaryInstructionBase(debugLoc, beginApplyToken, Ty) {
assert(isaResultOf<BeginApplyInst>(beginApplyToken) &&
isaResultOf<BeginApplyInst>(beginApplyToken)->isBeginApplyToken());
}
Expand Down
4 changes: 2 additions & 2 deletions include/swift/SIL/SILNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,8 @@ ABSTRACT_VALUE_AND_INST(SingleValueInstruction, ValueBase, SILInstruction)
SingleValueInstruction, MayHaveSideEffects, MayRelease)
SINGLE_VALUE_INST(PartialApplyInst, partial_apply,
SingleValueInstruction, MayHaveSideEffects, DoesNotRelease)
SINGLE_VALUE_INST(EndApplyInst, end_apply,
SILInstruction, MayHaveSideEffects, MayRelease)
SINGLE_VALUE_INST(FunctionExtractIsolationInst, function_extract_isolation,
SingleValueInstruction, None, DoesNotRelease)

Expand Down Expand Up @@ -873,8 +875,6 @@ NON_VALUE_INST(UncheckedRefCastAddrInst, unchecked_ref_cast_addr,
SILInstruction, MayHaveSideEffects, DoesNotRelease)
NON_VALUE_INST(AllocGlobalInst, alloc_global,
SILInstruction, MayHaveSideEffects, DoesNotRelease)
NON_VALUE_INST(EndApplyInst, end_apply,
SILInstruction, MayHaveSideEffects, MayRelease)
NON_VALUE_INST(AbortApplyInst, abort_apply,
SILInstruction, MayHaveSideEffects, MayRelease)
NON_VALUE_INST(PackElementSetInst, pack_element_set,
Expand Down
39 changes: 19 additions & 20 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4524,29 +4524,29 @@ SILFunctionType::SILFunctionType(
!ext.getLifetimeDependenceInfo().empty();
Bits.SILFunctionType.CoroutineKind = unsigned(coroutineKind);
NumParameters = params.size();
if (coroutineKind == SILCoroutineKind::None) {
assert(yields.empty());
NumAnyResults = normalResults.size();
NumAnyIndirectFormalResults = 0;
NumPackResults = 0;
for (auto &resultInfo : normalResults) {
if (resultInfo.isFormalIndirect())
NumAnyIndirectFormalResults++;
if (resultInfo.isPack())
NumPackResults++;
}
memcpy(getMutableResults().data(), normalResults.data(),
normalResults.size() * sizeof(SILResultInfo));
} else {
assert(normalResults.empty());
NumAnyResults = yields.size();
NumAnyIndirectFormalResults = 0;
assert((coroutineKind == SILCoroutineKind::None && yields.empty()) ||
coroutineKind != SILCoroutineKind::None);

NumAnyResults = normalResults.size();
NumAnyIndirectFormalResults = 0;
NumPackResults = 0;
for (auto &resultInfo : normalResults) {
if (resultInfo.isFormalIndirect())
NumAnyIndirectFormalResults++;
if (resultInfo.isPack())
NumPackResults++;
}
memcpy(getMutableResults().data(), normalResults.data(),
normalResults.size() * sizeof(SILResultInfo));
if (coroutineKind != SILCoroutineKind::None) {
NumAnyYieldResults = yields.size();
NumAnyIndirectFormalYieldResults = 0;
NumPackResults = 0;
for (auto &yieldInfo : yields) {
if (yieldInfo.isFormalIndirect())
NumAnyIndirectFormalResults++;
NumAnyIndirectFormalYieldResults++;
if (yieldInfo.isPack())
NumPackResults++;
NumPackYieldResults++;
}
memcpy(getMutableYields().data(), yields.data(),
yields.size() * sizeof(SILYieldInfo));
Expand Down Expand Up @@ -4718,7 +4718,6 @@ CanSILFunctionType SILFunctionType::get(
std::optional<SILResultInfo> errorResult, SubstitutionMap patternSubs,
SubstitutionMap invocationSubs, const ASTContext &ctx,
ProtocolConformanceRef witnessMethodConformance) {
assert(coroutineKind == SILCoroutineKind::None || normalResults.empty());
assert(coroutineKind != SILCoroutineKind::None || yields.empty());
assert(!ext.isPseudogeneric() || genericSig ||
coroutineKind != SILCoroutineKind::None);
Expand Down
Loading