Skip to content

Commit b87b42c

Browse files
committed
[CoroutineAccessors] If available, provide old ABI
If the feature is enabled, base the requirement for the underscored accessors on the availability of the non-underscored accessors. If the (non-underscored) accessor's was available earlier than the feature, interpret that to mean that the underscored version was available in that earlier version, and require the underscored version. The goal is to ensure that the ABI is preserved, so long as the simplest migration is done (namely, deleting the underscores from the old accessors). For modify2, cache the required-ness in the same way that it is cached for modify.
1 parent 651436d commit b87b42c

File tree

7 files changed

+139
-251
lines changed

7 files changed

+139
-251
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5765,6 +5765,8 @@ class AbstractStorageDecl : public ValueDecl {
57655765
unsigned RequiresOpaqueAccessors : 1;
57665766
unsigned RequiresOpaqueModifyCoroutineComputed : 1;
57675767
unsigned RequiresOpaqueModifyCoroutine : 1;
5768+
unsigned RequiresOpaqueModify2CoroutineComputed : 1;
5769+
unsigned RequiresOpaqueModify2Coroutine : 1;
57685770
} LazySemanticInfo = { };
57695771

57705772
/// The implementation info for the accessors.
@@ -6046,6 +6048,11 @@ class AbstractStorageDecl : public ValueDecl {
60466048
/// set?
60476049
bool requiresOpaqueModify2Coroutine() const;
60486050

6051+
/// Given that CoroutineAccessors is enabled, is _read/_modify required for
6052+
/// ABI stability?
6053+
bool
6054+
requiresCorrespondingUnderscoredCoroutineAccessor(AccessorKind kind) const;
6055+
60496056
/// Does this storage have any explicit observers (willSet or didSet) attached
60506057
/// to it?
60516058
bool hasObservers() const {

include/swift/AST/FeatureAvailability.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ FEATURE(ClearSensitive, FUTURE)
8080
FEATURE(UpdatePureObjCClassMetadata, FUTURE)
8181
FEATURE(ValueGenericType, FUTURE)
8282
FEATURE(InitRawStructMetadata2, FUTURE)
83+
// TODO: CoroutineAccessors: Change to correct version number.
84+
FEATURE(CoroutineAccessors, FUTURE)
8385

8486
#undef FEATURE
8587
#undef FUTURE

include/swift/AST/TypeCheckRequests.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,19 +1937,19 @@ class RequiresOpaqueAccessorsRequest :
19371937
void cacheResult(bool value) const;
19381938
};
19391939

1940-
class RequiresOpaqueModifyCoroutineRequest :
1941-
public SimpleRequest<RequiresOpaqueModifyCoroutineRequest,
1942-
bool(AbstractStorageDecl *),
1943-
RequestFlags::SeparatelyCached> {
1940+
class RequiresOpaqueModifyCoroutineRequest
1941+
: public SimpleRequest<RequiresOpaqueModifyCoroutineRequest,
1942+
bool(AbstractStorageDecl *, bool isUnderscored),
1943+
RequestFlags::SeparatelyCached> {
19441944
public:
19451945
using SimpleRequest::SimpleRequest;
19461946

19471947
private:
19481948
friend SimpleRequest;
19491949

19501950
// Evaluation.
1951-
bool
1952-
evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
1951+
bool evaluate(Evaluator &evaluator, AbstractStorageDecl *decl,
1952+
bool isUnderscored) const;
19531953

19541954
public:
19551955
// Separate caching.

lib/AST/Decl.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,6 +3018,10 @@ bool AbstractStorageDecl::requiresOpaqueAccessor(AccessorKind kind) const {
30183018
}
30193019

30203020
bool AbstractStorageDecl::requiresOpaqueReadCoroutine() const {
3021+
ASTContext &ctx = getASTContext();
3022+
if (ctx.LangOpts.hasFeature(Feature::CoroutineAccessors))
3023+
return requiresCorrespondingUnderscoredCoroutineAccessor(
3024+
AccessorKind::Read2);
30213025
return getOpaqueReadOwnership() != OpaqueReadOwnership::Owned;
30223026
}
30233027

@@ -3030,19 +3034,20 @@ bool AbstractStorageDecl::requiresOpaqueRead2Coroutine() const {
30303034

30313035
bool AbstractStorageDecl::requiresOpaqueModifyCoroutine() const {
30323036
ASTContext &ctx = getASTContext();
3033-
return evaluateOrDefault(ctx.evaluator,
3034-
RequiresOpaqueModifyCoroutineRequest{const_cast<AbstractStorageDecl *>(this)},
3035-
false);
3037+
return evaluateOrDefault(
3038+
ctx.evaluator,
3039+
RequiresOpaqueModifyCoroutineRequest{
3040+
const_cast<AbstractStorageDecl *>(this), /*isUnderscored=*/true},
3041+
false);
30363042
}
30373043

30383044
bool AbstractStorageDecl::requiresOpaqueModify2Coroutine() const {
30393045
ASTContext &ctx = getASTContext();
3040-
if (!ctx.LangOpts.hasFeature(Feature::CoroutineAccessors))
3041-
return false;
3042-
return evaluateOrDefault(ctx.evaluator,
3043-
RequiresOpaqueModifyCoroutineRequest{
3044-
const_cast<AbstractStorageDecl *>(this)},
3045-
false);
3046+
return evaluateOrDefault(
3047+
ctx.evaluator,
3048+
RequiresOpaqueModifyCoroutineRequest{
3049+
const_cast<AbstractStorageDecl *>(this), /*isUnderscored=*/false},
3050+
false);
30463051
}
30473052

30483053
AccessorDecl *AbstractStorageDecl::getSynthesizedAccessor(AccessorKind kind) const {

lib/AST/TypeCheckRequests.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -795,15 +795,29 @@ void RequiresOpaqueAccessorsRequest::cacheResult(bool value) const {
795795
std::optional<bool>
796796
RequiresOpaqueModifyCoroutineRequest::getCachedResult() const {
797797
auto *storage = std::get<0>(getStorage());
798-
if (storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed)
799-
return static_cast<bool>(storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine);
798+
auto isUnderscored = std::get<1>(getStorage());
799+
if (isUnderscored) {
800+
if (storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed)
801+
return static_cast<bool>(
802+
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine);
803+
} else {
804+
if (storage->LazySemanticInfo.RequiresOpaqueModify2CoroutineComputed)
805+
return static_cast<bool>(
806+
storage->LazySemanticInfo.RequiresOpaqueModify2Coroutine);
807+
}
800808
return std::nullopt;
801809
}
802810

803811
void RequiresOpaqueModifyCoroutineRequest::cacheResult(bool value) const {
804812
auto *storage = std::get<0>(getStorage());
805-
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed = 1;
806-
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine = value;
813+
auto isUnderscored = std::get<1>(getStorage());
814+
if (isUnderscored) {
815+
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutineComputed = 1;
816+
storage->LazySemanticInfo.RequiresOpaqueModifyCoroutine = value;
817+
} else {
818+
storage->LazySemanticInfo.RequiresOpaqueModify2CoroutineComputed = 1;
819+
storage->LazySemanticInfo.RequiresOpaqueModify2Coroutine = value;
820+
}
807821
}
808822

809823
//----------------------------------------------------------------------------//

lib/Sema/TypeCheckStorage.cpp

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,9 +2710,67 @@ RequiresOpaqueAccessorsRequest::evaluate(Evaluator &evaluator,
27102710
return true;
27112711
}
27122712

2713-
bool
2714-
RequiresOpaqueModifyCoroutineRequest::evaluate(Evaluator &evaluator,
2715-
AbstractStorageDecl *storage) const {
2713+
/// When the CoroutineAccessors feature is available, the coroutine accessor
2714+
/// _could_ be required. That non-underscored accessor would be preferred to
2715+
/// its underscored counterpart accessor.
2716+
///
2717+
/// The underscored accessor could, however, still be required for ABI
2718+
/// stability.
2719+
bool AbstractStorageDecl::requiresCorrespondingUnderscoredCoroutineAccessor(
2720+
AccessorKind kind) const {
2721+
auto &ctx = getASTContext();
2722+
assert(ctx.LangOpts.hasFeature(Feature::CoroutineAccessors));
2723+
assert(kind == AccessorKind::Modify2 || kind == AccessorKind::Read2);
2724+
2725+
// Non-stable modules have no ABI to keep stable.
2726+
if (getModuleContext()->getResilienceStrategy() !=
2727+
ResilienceStrategy::Resilient)
2728+
return false;
2729+
2730+
// Non-exported storage has no ABI to keep stable.
2731+
if (!isExported(this))
2732+
return false;
2733+
2734+
// The the non-underscored accessor is not present, the underscored accessor
2735+
// won't be either.
2736+
// TODO: CoroutineAccessors: What if only the underscored is written out?
2737+
auto *accessor = getOpaqueAccessor(kind);
2738+
if (!accessor)
2739+
return false;
2740+
2741+
// Availability checks are only relevant on targets which support versioned
2742+
// availability. Otherwise, since we're building with library evolution,
2743+
// conservatively assume that the binary must keep ABI compatibility with its
2744+
// prior versions, and emit the underscored variant.
2745+
if (!ctx.supportsVersionedAvailability())
2746+
return true;
2747+
2748+
auto modifyAvailability = TypeChecker::availabilityAtLocation({}, accessor);
2749+
auto featureAvailability = ctx.getCoroutineAccessorsRuntimeAvailability();
2750+
// If accessor was introduced only after the feature was, there's no old ABI
2751+
// to maintain.
2752+
if (modifyAvailability.getPlatformRange().isContainedIn(featureAvailability))
2753+
return false;
2754+
2755+
// The underscored accessor is required for ABI stability.
2756+
return true;
2757+
}
2758+
2759+
bool RequiresOpaqueModifyCoroutineRequest::evaluate(
2760+
Evaluator &evaluator, AbstractStorageDecl *storage,
2761+
bool isUnderscored) const {
2762+
auto &ctx = storage->getASTContext();
2763+
bool hasModifyFeature = ctx.LangOpts.hasFeature(Feature::CoroutineAccessors);
2764+
2765+
// No `modify` accessor without the feature.
2766+
if (!hasModifyFeature && !isUnderscored)
2767+
return false;
2768+
2769+
if (hasModifyFeature && isUnderscored) {
2770+
return storage->requiresCorrespondingUnderscoredCoroutineAccessor(
2771+
AccessorKind::Modify2);
2772+
}
2773+
27162774
// Only for mutable storage.
27172775
if (!storage->supportsMutation())
27182776
return false;

0 commit comments

Comments
 (0)