Skip to content

Commit c28d295

Browse files
committed
[CoroutineAccessors] Require underscored override.
If an overridden decl requires an underscored accessor, then the derived decl requires one too. Otherwise dispatch to a less-derived instance could bind to the underscored accessor which lacks an override. rdar://149352777
1 parent 2ec007d commit c28d295

File tree

2 files changed

+82
-5
lines changed

2 files changed

+82
-5
lines changed

lib/Sema/TypeCheckStorage.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,11 +2672,28 @@ RequiresOpaqueAccessorsRequest::evaluate(Evaluator &evaluator,
26722672
/// stability.
26732673
static bool requiresCorrespondingUnderscoredCoroutineAccessorImpl(
26742674
AbstractStorageDecl const *storage, AccessorKind kind,
2675-
AccessorDecl const *decl) {
2675+
AccessorDecl const *decl, AbstractStorageDecl const *derived) {
26762676
auto &ctx = storage->getASTContext();
26772677
assert(ctx.LangOpts.hasFeature(Feature::CoroutineAccessors));
26782678
assert(kind == AccessorKind::Modify2 || kind == AccessorKind::Read2);
26792679

2680+
// If any overridden decl requires the underscored version, then this decl
2681+
// does too. Otherwise dispatch to the underscored version on a value
2682+
// statically the super but dynamically this subtype would not dispatch to an
2683+
// override of the underscored version but rather (incorrectly) the
2684+
// supertype's implementation.
2685+
if (storage == derived) {
2686+
auto *current = storage;
2687+
while ((current = current->getOverriddenDecl())) {
2688+
auto *currentDecl = cast_or_null<AccessorDecl>(
2689+
decl ? decl->getOverriddenDecl() : nullptr);
2690+
if (requiresCorrespondingUnderscoredCoroutineAccessorImpl(
2691+
current, kind, currentDecl, derived)) {
2692+
return true;
2693+
}
2694+
}
2695+
}
2696+
26802697
// Non-stable modules have no ABI to keep stable.
26812698
if (storage->getModuleContext()->getResilienceStrategy() !=
26822699
ResilienceStrategy::Resilient)
@@ -2699,11 +2716,30 @@ static bool requiresCorrespondingUnderscoredCoroutineAccessorImpl(
26992716
if (!ctx.supportsVersionedAvailability())
27002717
return true;
27012718

2702-
auto modifyAvailability = AvailabilityContext::forLocation({}, accessor);
2719+
AvailabilityContext accessorAvailability = [&] {
2720+
if (storage->getModuleContext()->isMainModule()) {
2721+
return AvailabilityContext::forDeclSignature(accessor);
2722+
}
2723+
// Calculate the availability of the imported declaration ourselves starting
2724+
// from always available and constraining by walking the enclosing decl
2725+
// contexts.
2726+
auto retval = AvailabilityContext::forAlwaysAvailable(ctx);
2727+
auto declContext = storage->getInnermostDeclContext();
2728+
while (declContext) {
2729+
const Decl *decl = declContext->getInnermostDeclarationDeclContext();
2730+
if (!decl)
2731+
break;
2732+
2733+
retval.constrainWithDecl(decl);
2734+
declContext = decl->getDeclContext();
2735+
}
2736+
return retval;
2737+
}();
27032738
auto featureAvailability = ctx.getCoroutineAccessorsAvailability();
27042739
// If accessor was introduced only after the feature was, there's no old ABI
27052740
// to maintain.
2706-
if (modifyAvailability.getPlatformRange().isContainedIn(featureAvailability))
2741+
if (accessorAvailability.getPlatformRange().isContainedIn(
2742+
featureAvailability))
27072743
return false;
27082744

27092745
// The underscored accessor is required for ABI stability.
@@ -2712,8 +2748,9 @@ static bool requiresCorrespondingUnderscoredCoroutineAccessorImpl(
27122748

27132749
bool AbstractStorageDecl::requiresCorrespondingUnderscoredCoroutineAccessor(
27142750
AccessorKind kind, AccessorDecl const *decl) const {
2715-
return requiresCorrespondingUnderscoredCoroutineAccessorImpl(this, kind,
2716-
decl);
2751+
return requiresCorrespondingUnderscoredCoroutineAccessorImpl(
2752+
this, kind, decl,
2753+
/*derived=*/this);
27172754
}
27182755

27192756
bool RequiresOpaqueModifyCoroutineRequest::evaluate(

test/SILGen/coroutine_accessors_availability.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,21 @@ public func modifyOldNoninlinableNew(_ n: inout StructOld) {
223223
public func takeInt(_ i: Int) {
224224
}
225225

226+
open class BaseClassOld {
227+
public init(_ i : Int) {
228+
self._i = i
229+
}
230+
var _i: Int
231+
open var i: Int {
232+
read {
233+
yield _i
234+
}
235+
modify {
236+
yield &_i
237+
}
238+
}
239+
}
240+
226241
//--- Downstream.swift
227242

228243
import Library
@@ -348,3 +363,28 @@ func modifyOldOld() {
348363

349364
n.i.increment()
350365
}
366+
367+
public class DerivedOldFromBaseClassOld : BaseClassOld {
368+
public init(_ i : Int, _ j : Int) {
369+
self._j = j
370+
super.init(i)
371+
}
372+
var _j: Int
373+
override public var i: Int {
374+
read {
375+
yield _j
376+
}
377+
modify {
378+
yield &_j
379+
}
380+
}
381+
}
382+
383+
// CHECK-LABEL: sil_vtable [serialized] DerivedOldFromBaseClassOld {
384+
// CHECK-NEXT: #BaseClassOld.init!allocator
385+
// CHECK-NEXT: #BaseClassOld.i!read
386+
// CHECK-NEXT: #BaseClassOld.i!read2
387+
// CHECK-NEXT: #BaseClassOld.i!setter
388+
// CHECK-NEXT: #BaseClassOld.i!modify
389+
// CHECK-NEXT: #BaseClassOld.i!modify2
390+
// CHECK: }

0 commit comments

Comments
 (0)