Skip to content

[CoroutineAccessors] Key table membership off availability. #77265

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 12 commits into from
Oct 31, 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
21 changes: 17 additions & 4 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5765,6 +5765,8 @@ class AbstractStorageDecl : public ValueDecl {
unsigned RequiresOpaqueAccessors : 1;
unsigned RequiresOpaqueModifyCoroutineComputed : 1;
unsigned RequiresOpaqueModifyCoroutine : 1;
unsigned RequiresOpaqueModify2CoroutineComputed : 1;
unsigned RequiresOpaqueModify2Coroutine : 1;
} LazySemanticInfo = { };

/// The implementation info for the accessors.
Expand Down Expand Up @@ -6029,17 +6031,28 @@ class AbstractStorageDecl : public ValueDecl {
return getOpaqueReadOwnership() != OpaqueReadOwnership::Borrowed;
}

/// Does this storage require a '_read' accessor in its opaque-accessors set?
bool requiresOpaqueReadCoroutine() const;

/// Does this storage require a 'read' accessor in its opaque-accessors set?
bool requiresOpaqueReadCoroutine() const {
return getOpaqueReadOwnership() != OpaqueReadOwnership::Owned;
}
bool requiresOpaqueRead2Coroutine() const;

/// Does this storage require a 'set' accessor in its opaque-accessors set?
bool requiresOpaqueSetter() const { return supportsMutation(); }

/// Does this storage require a 'modify' accessor in its opaque-accessors set?
/// Does this storage require a '_modify' accessor in its opaque-accessors
/// set?
bool requiresOpaqueModifyCoroutine() const;

/// Does this storage require a 'modify' accessor in its opaque-accessors
/// set?
bool requiresOpaqueModify2Coroutine() const;

/// Given that CoroutineAccessors is enabled, is _read/_modify required for
/// ABI stability?
bool
requiresCorrespondingUnderscoredCoroutineAccessor(AccessorKind kind) const;

/// Does this storage have any explicit observers (willSet or didSet) attached
/// to it?
bool hasObservers() const {
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/FeatureAvailability.def
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ FEATURE(ClearSensitive, FUTURE)
FEATURE(UpdatePureObjCClassMetadata, FUTURE)
FEATURE(ValueGenericType, FUTURE)
FEATURE(InitRawStructMetadata2, FUTURE)
// TODO: CoroutineAccessors: Change to correct version number.
FEATURE(CoroutineAccessors, FUTURE)

#undef FEATURE
#undef FUTURE
12 changes: 6 additions & 6 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1937,19 +1937,19 @@ class RequiresOpaqueAccessorsRequest :
void cacheResult(bool value) const;
};

class RequiresOpaqueModifyCoroutineRequest :
public SimpleRequest<RequiresOpaqueModifyCoroutineRequest,
bool(AbstractStorageDecl *),
RequestFlags::SeparatelyCached> {
class RequiresOpaqueModifyCoroutineRequest
: public SimpleRequest<RequiresOpaqueModifyCoroutineRequest,
bool(AbstractStorageDecl *, bool isUnderscored),
RequestFlags::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
bool
evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
bool evaluate(Evaluator &evaluator, AbstractStorageDecl *decl,
bool isUnderscored) const;

public:
// Separate caching.
Expand Down
55 changes: 49 additions & 6 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2849,8 +2849,13 @@ getDirectReadWriteAccessStrategy(const AbstractStorageDecl *storage) {
/*dispatch*/ false);
case ReadWriteImplKind::StoredWithDidSet:
case ReadWriteImplKind::InheritedWithDidSet:
if (storage->requiresOpaqueModifyCoroutine() &&
if (storage->requiresOpaqueModify2Coroutine() &&
storage->getParsedAccessor(AccessorKind::DidSet)->isSimpleDidSet()) {
return AccessStrategy::getAccessor(AccessorKind::Modify2,
/*dispatch*/ false);
} else if (storage->requiresOpaqueModifyCoroutine() &&
storage->getParsedAccessor(AccessorKind::DidSet)
->isSimpleDidSet()) {
return AccessStrategy::getAccessor(AccessorKind::Modify,
/*dispatch*/ false);
} else {
Expand All @@ -2868,6 +2873,8 @@ getDirectReadWriteAccessStrategy(const AbstractStorageDecl *storage) {

static AccessStrategy
getOpaqueReadAccessStrategy(const AbstractStorageDecl *storage, bool dispatch) {
if (storage->requiresOpaqueRead2Coroutine())
return AccessStrategy::getAccessor(AccessorKind::Read2, dispatch);
if (storage->requiresOpaqueReadCoroutine())
return AccessStrategy::getAccessor(AccessorKind::Read, dispatch);
return AccessStrategy::getAccessor(AccessorKind::Get, dispatch);
Expand All @@ -2883,6 +2890,8 @@ getOpaqueWriteAccessStrategy(const AbstractStorageDecl *storage, bool dispatch)
static AccessStrategy
getOpaqueReadWriteAccessStrategy(const AbstractStorageDecl *storage,
bool dispatch) {
if (storage->requiresOpaqueModify2Coroutine())
return AccessStrategy::getAccessor(AccessorKind::Modify2, dispatch);
if (storage->requiresOpaqueModifyCoroutine())
return AccessStrategy::getAccessor(AccessorKind::Modify, dispatch);
return AccessStrategy::getMaterializeToTemporary(
Expand Down Expand Up @@ -2990,11 +2999,13 @@ bool AbstractStorageDecl::requiresOpaqueAccessor(AccessorKind kind) const {
case AccessorKind::Set:
return requiresOpaqueSetter();
case AccessorKind::Read:
case AccessorKind::Read2:
return requiresOpaqueReadCoroutine();
case AccessorKind::Read2:
return requiresOpaqueRead2Coroutine();
case AccessorKind::Modify:
case AccessorKind::Modify2:
return requiresOpaqueModifyCoroutine();
case AccessorKind::Modify2:
return requiresOpaqueModify2Coroutine();

// Other accessors are never part of the opaque-accessors set.
#define OPAQUE_ACCESSOR(ID, KEYWORD)
Expand All @@ -3006,11 +3017,37 @@ bool AbstractStorageDecl::requiresOpaqueAccessor(AccessorKind kind) const {
llvm_unreachable("bad accessor kind");
}

bool AbstractStorageDecl::requiresOpaqueReadCoroutine() const {
ASTContext &ctx = getASTContext();
if (ctx.LangOpts.hasFeature(Feature::CoroutineAccessors))
return requiresCorrespondingUnderscoredCoroutineAccessor(
AccessorKind::Read2);
return getOpaqueReadOwnership() != OpaqueReadOwnership::Owned;
}

bool AbstractStorageDecl::requiresOpaqueRead2Coroutine() const {
ASTContext &ctx = getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::CoroutineAccessors))
return false;
return getOpaqueReadOwnership() != OpaqueReadOwnership::Owned;
}

bool AbstractStorageDecl::requiresOpaqueModifyCoroutine() const {
ASTContext &ctx = getASTContext();
return evaluateOrDefault(ctx.evaluator,
RequiresOpaqueModifyCoroutineRequest{const_cast<AbstractStorageDecl *>(this)},
false);
return evaluateOrDefault(
ctx.evaluator,
RequiresOpaqueModifyCoroutineRequest{
const_cast<AbstractStorageDecl *>(this), /*isUnderscored=*/true},
false);
}

bool AbstractStorageDecl::requiresOpaqueModify2Coroutine() const {
ASTContext &ctx = getASTContext();
return evaluateOrDefault(
ctx.evaluator,
RequiresOpaqueModifyCoroutineRequest{
const_cast<AbstractStorageDecl *>(this), /*isUnderscored=*/false},
false);
}

AccessorDecl *AbstractStorageDecl::getSynthesizedAccessor(AccessorKind kind) const {
Expand Down Expand Up @@ -3109,13 +3146,19 @@ void AbstractStorageDecl::visitExpectedOpaqueAccessors(
if (requiresOpaqueReadCoroutine())
visit(AccessorKind::Read);

if (requiresOpaqueRead2Coroutine())
visit(AccessorKind::Read2);

// All mutable storage should have a setter.
if (requiresOpaqueSetter())
visit(AccessorKind::Set);

// Include the modify coroutine if it's required.
if (requiresOpaqueModifyCoroutine())
visit(AccessorKind::Modify);

if (requiresOpaqueModify2Coroutine())
visit(AccessorKind::Modify2);
}

void AbstractStorageDecl::visitOpaqueAccessors(
Expand Down
22 changes: 18 additions & 4 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -795,15 +795,29 @@ void RequiresOpaqueAccessorsRequest::cacheResult(bool value) const {
std::optional<bool>
RequiresOpaqueModifyCoroutineRequest::getCachedResult() const {
auto *storage = std::get<0>(getStorage());
if (storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed)
return static_cast<bool>(storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine);
auto isUnderscored = std::get<1>(getStorage());
if (isUnderscored) {
if (storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed)
return static_cast<bool>(
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine);
} else {
if (storage->LazySemanticInfo.RequiresOpaqueModify2CoroutineComputed)
return static_cast<bool>(
storage->LazySemanticInfo.RequiresOpaqueModify2Coroutine);
}
return std::nullopt;
}

void RequiresOpaqueModifyCoroutineRequest::cacheResult(bool value) const {
auto *storage = std::get<0>(getStorage());
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed = 1;
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine = value;
auto isUnderscored = std::get<1>(getStorage());
if (isUnderscored) {
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed = 1;
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine = value;
} else {
storage->LazySemanticInfo.RequiresOpaqueModify2CoroutineComputed = 1;
storage->LazySemanticInfo.RequiresOpaqueModify2Coroutine = value;
}
}

//----------------------------------------------------------------------------//
Expand Down
19 changes: 15 additions & 4 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6000,7 +6000,8 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn,
return normalBB->createPhiArgument(resultType, OwnershipKind::Owned);
}

std::pair<MultipleValueInstructionResult *, CleanupHandle>
std::tuple<MultipleValueInstructionResult *, CleanupHandle, SILValue,
CleanupHandle>
SILGenFunction::emitBeginApplyWithRethrow(SILLocation loc, SILValue fn,
SILType substFnType,
SubstitutionMap subs,
Expand All @@ -6021,19 +6022,29 @@ SILGenFunction::emitBeginApplyWithRethrow(SILLocation loc, SILValue fn,

auto *token = beginApply->getTokenResult();

CleanupHandle deallocCleanup;
auto *allocation = beginApply->getCalleeAllocationResult();
if (allocation) {
deallocCleanup = enterDeallocStackCleanup(allocation);
}

Cleanups.pushCleanup<EndCoroutineApply>(token);
auto abortCleanup = Cleanups.getTopCleanup();

return { token, abortCleanup };
return {token, abortCleanup, allocation, deallocCleanup};
}

void SILGenFunction::emitEndApplyWithRethrow(SILLocation loc,
MultipleValueInstructionResult *token) {
void SILGenFunction::emitEndApplyWithRethrow(
SILLocation loc, MultipleValueInstructionResult *token,
SILValue allocation) {
// TODO: adjust this to handle results of TryBeginApplyInst.
assert(token->isBeginApplyToken());

B.createEndApply(loc, token,
SILType::getEmptyTupleType(getASTContext()));
if (allocation) {
B.createDeallocStack(loc, allocation);
}
}

void SILGenFunction::emitYield(SILLocation loc,
Expand Down
6 changes: 4 additions & 2 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2185,12 +2185,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
SubstitutionMap subs,
ArrayRef<SILValue> args);

std::pair<MultipleValueInstructionResult *, CleanupHandle>
std::tuple<MultipleValueInstructionResult *, CleanupHandle, SILValue,
CleanupHandle>
emitBeginApplyWithRethrow(SILLocation loc, SILValue fn, SILType substFnType,
SubstitutionMap subs, ArrayRef<SILValue> args,
SmallVectorImpl<SILValue> &yields);
void emitEndApplyWithRethrow(SILLocation loc,
MultipleValueInstructionResult *token);
MultipleValueInstructionResult *token,
SILValue allocation);

ManagedValue emitExtractFunctionIsolation(SILLocation loc,
ArgumentSource &&fnValue);
Expand Down
34 changes: 23 additions & 11 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6824,10 +6824,13 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
case SILCoroutineKind::YieldOnce:
case SILCoroutineKind::YieldOnce2: {
SmallVector<SILValue, 4> derivedYields;
auto tokenAndCleanup =
emitBeginApplyWithRethrow(loc, derivedRef,
SILType::getPrimitiveObjectType(derivedFTy),
subs, args, derivedYields);
auto tokenAndCleanups = emitBeginApplyWithRethrow(
loc, derivedRef, SILType::getPrimitiveObjectType(derivedFTy), subs,
args, derivedYields);
auto token = std::get<0>(tokenAndCleanups);
auto abortCleanup = std::get<1>(tokenAndCleanups);
auto allocation = std::get<2>(tokenAndCleanups);
auto deallocCleanup = std::get<3>(tokenAndCleanups);
auto overrideSubs = SubstitutionMap::getOverrideSubstitutions(
base.getDecl(), derived.getDecl()).subst(subs);

Expand All @@ -6837,10 +6840,13 @@ SILGenFunction::emitVTableThunk(SILDeclRef base,
translateYields(*this, loc, derivedYields, derivedYieldInfo, baseYieldInfo);

// Kill the abort cleanup without emitting it.
Cleanups.setCleanupState(tokenAndCleanup.second, CleanupState::Dead);
Cleanups.setCleanupState(abortCleanup, CleanupState::Dead);
if (allocation) {
Cleanups.setCleanupState(deallocCleanup, CleanupState::Dead);
}

// End the inner coroutine normally.
emitEndApplyWithRethrow(loc, tokenAndCleanup.first);
emitEndApplyWithRethrow(loc, token, allocation);

result = B.createTuple(loc, {});
break;
Expand Down Expand Up @@ -7213,9 +7219,12 @@ void SILGenFunction::emitProtocolWitness(
case SILCoroutineKind::YieldOnce:
case SILCoroutineKind::YieldOnce2: {
SmallVector<SILValue, 4> witnessYields;
auto tokenAndCleanup =
emitBeginApplyWithRethrow(loc, witnessFnRef, witnessSILTy, witnessSubs,
args, witnessYields);
auto tokenAndCleanups = emitBeginApplyWithRethrow(
loc, witnessFnRef, witnessSILTy, witnessSubs, args, witnessYields);
auto token = std::get<0>(tokenAndCleanups);
auto abortCleanup = std::get<1>(tokenAndCleanups);
auto allocation = std::get<2>(tokenAndCleanups);
auto deallocCleanup = std::get<3>(tokenAndCleanups);

YieldInfo witnessYieldInfo(SGM, witness, witnessFTy, witnessSubs);
YieldInfo reqtYieldInfo(SGM, requirement, thunkTy,
Expand All @@ -7224,10 +7233,13 @@ void SILGenFunction::emitProtocolWitness(
translateYields(*this, loc, witnessYields, witnessYieldInfo, reqtYieldInfo);

// Kill the abort cleanup without emitting it.
Cleanups.setCleanupState(tokenAndCleanup.second, CleanupState::Dead);
Cleanups.setCleanupState(abortCleanup, CleanupState::Dead);
if (allocation) {
Cleanups.setCleanupState(deallocCleanup, CleanupState::Dead);
}

// End the inner coroutine normally.
emitEndApplyWithRethrow(loc, tokenAndCleanup.first);
emitEndApplyWithRethrow(loc, token, allocation);

reqtResultValue = B.createTuple(loc, {});
break;
Expand Down
Loading