Skip to content

Commit c4aed1a

Browse files
authored
Merge pull request #20426 from rjmccall/class-vs-protocol-borrowed-fix
Fix a bug with class vs. protocol @_borrowed mismatches
2 parents cc9c4fa + 4758b2f commit c4aed1a

File tree

4 files changed

+55
-27
lines changed

4 files changed

+55
-27
lines changed

lib/AST/ASTVerifier.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3064,10 +3064,9 @@ class Verifier : public ASTWalker {
30643064
abort();
30653065
}
30663066

3067-
if (AD->getAccessorKind() != AccessorKind::Read &&
3068-
AD->getAccessorKind() != AccessorKind::Modify) {
3069-
Out << "hasForcedStaticDispatch() set on accessor other than "
3070-
"read or modify\n";
3067+
if (AD->getStorage()->requiresOpaqueAccessor(AD->getAccessorKind())) {
3068+
Out << "hasForcedStaticDispatch() set on accessor that's opaque "
3069+
"for its storage\n";
30713070
abort();
30723071
}
30733072
}

lib/Sema/CodeSynthesis.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ static AccessorDecl *createGetterPrototype(TypeChecker &TC,
255255
if (storage->isStatic())
256256
getter->setStatic();
257257

258+
if (!storage->requiresOpaqueAccessor(AccessorKind::Get))
259+
getter->setForcedStaticDispatch(true);
260+
258261
// Always add the getter to the context immediately after the storage.
259262
addMemberToContextIfNeeded(getter, storage->getDeclContext(), storage);
260263

@@ -298,6 +301,9 @@ static AccessorDecl *createSetterPrototype(TypeChecker &TC,
298301
if (isStatic)
299302
setter->setStatic();
300303

304+
// All mutable storage requires a setter.
305+
assert(storage->requiresOpaqueAccessor(AccessorKind::Set));
306+
301307
// Always add the setter to the context immediately after the getter.
302308
if (!getter) getter = storage->getGetter();
303309
if (!getter) getter = storage->getReadCoroutine();
@@ -307,14 +313,6 @@ static AccessorDecl *createSetterPrototype(TypeChecker &TC,
307313
return setter;
308314
}
309315

310-
// True if the storage is dynamic or imported from Objective-C. In these cases,
311-
// we need to emit static coroutine accessors that dynamically dispatch
312-
// to 'get' and 'set', rather than the normal dynamically dispatched
313-
// opaque accessors that peer dispatch to 'get' and 'set'.
314-
static bool needsDynamicCoroutineAccessors(AbstractStorageDecl *storage) {
315-
return storage->isObjCDynamic() || storage->hasClangNode();
316-
}
317-
318316
/// Mark the accessor as transparent if we can.
319317
///
320318
/// If the storage is inside a fixed-layout nominal type, we can mark the
@@ -418,11 +416,11 @@ createCoroutineAccessorPrototype(TypeChecker &TC,
418416
if (storage->isFinal())
419417
makeFinal(ctx, accessor);
420418

421-
// If the storage is dynamic or ObjC-native, we can't add a dynamically-
422-
// dispatched method entry for the accessor, so force it to be
423-
// statically dispatched. ("final" would be inappropriate because the
424-
// property can still be overridden.)
425-
if (needsDynamicCoroutineAccessors(storage))
419+
// If the storage does not provide this accessor as an opaque accessor,
420+
// we can't add a dynamically-dispatched method entry for the accessor,
421+
// so force it to be statically dispatched. ("final" would be inappropriate
422+
// because the property can still be overridden.)
423+
if (!storage->requiresOpaqueAccessor(kind))
426424
accessor->setForcedStaticDispatch(true);
427425

428426
// Make sure the coroutine is available enough to access
@@ -2093,6 +2091,11 @@ void swift::maybeAddAccessorsToStorage(TypeChecker &TC,
20932091
}
20942092

20952093
static void synthesizeGetterBody(TypeChecker &TC, AccessorDecl *getter) {
2094+
if (getter->hasForcedStaticDispatch()) {
2095+
synthesizeTrivialGetterBody(TC, getter, TargetImpl::Ordinary);
2096+
return;
2097+
}
2098+
20962099
switch (getter->getStorage()->getReadImpl()) {
20972100
case ReadImplKind::Stored:
20982101
synthesizeTrivialGetterBody(TC, getter);

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,21 +1776,15 @@ OverriddenDeclsRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
17761776
auto baseAccessor = baseASD->getAccessor(kind);
17771777
if (!baseAccessor) continue;
17781778

1779-
switch (kind) {
1780-
case AccessorKind::Read:
1781-
if (baseASD->getReadCoroutine()->hasForcedStaticDispatch())
1782-
continue;
1783-
LLVM_FALLTHROUGH;
1779+
if (baseAccessor->hasForcedStaticDispatch())
1780+
continue;
17841781

1782+
switch (kind) {
17851783
case AccessorKind::Get:
1784+
case AccessorKind::Read:
17861785
break;
17871786

17881787
case AccessorKind::Modify:
1789-
if (baseASD->getModifyCoroutine()->hasForcedStaticDispatch())
1790-
continue;
1791-
1792-
LLVM_FALLTHROUGH;
1793-
17941788
case AccessorKind::Set:
17951789
// For setter accessors, we need the base's setter to be
17961790
// accessible from the overriding context, or it's not an override.

test/SILGen/read_accessor.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,35 @@ public struct TestBorrowedProperty {
173173
@_borrowed
174174
public var borrowedString = ""
175175
}
176+
177+
protocol ReadableTitle {
178+
@_borrowed
179+
var title: String { get }
180+
}
181+
class OverridableGetter : ReadableTitle {
182+
var title: String = ""
183+
}
184+
// The concrete read accessor is generated on-demand and does a class dispatch to the getter.
185+
// CHECK-LABEL: sil shared @$s13read_accessor17OverridableGetterC5titleSSvr
186+
// CHECK: class_method %0 : $OverridableGetter, #OverridableGetter.title!getter.1
187+
// CHECK-LABEL: // end sil function '$s13read_accessor17OverridableGetterC5titleSSvr'
188+
// The read witness thunk does a direct call to the concrete read accessor.
189+
// CHECK-LABEL: sil private [transparent] [thunk] @$s13read_accessor17OverridableGetterCAA13ReadableTitleA2aDP5titleSSvrTW
190+
// CHECK: function_ref @$s13read_accessor17OverridableGetterC5titleSSvr
191+
// CHECK-LABEL: // end sil function '$s13read_accessor17OverridableGetterCAA13ReadableTitleA2aDP5titleSSvrTW'
192+
193+
protocol GettableTitle {
194+
var title: String { get }
195+
}
196+
class OverridableReader : GettableTitle {
197+
@_borrowed
198+
var title: String = ""
199+
}
200+
// The concrete getter is generated on-demand and does a class dispatch to the read accessor.
201+
// CHECK-LABEL: sil shared @$s13read_accessor17OverridableReaderC5titleSSvg
202+
// CHECK: class_method %0 : $OverridableReader, #OverridableReader.title!read.1
203+
// CHECK-LABEL: // end sil function '$s13read_accessor17OverridableReaderC5titleSSvg'
204+
// The getter witness thunk does a direct call to the concrete getter.
205+
// CHECK-LABEL: sil private [transparent] [thunk] @$s13read_accessor17OverridableReaderCAA13GettableTitleA2aDP5titleSSvgTW
206+
// CHECK: function_ref @$s13read_accessor17OverridableReaderC5titleSSvg
207+
// CHECK-LABEL: // end sil function '$s13read_accessor17OverridableReaderCAA13GettableTitleA2aDP5titleSSvgTW'

0 commit comments

Comments
 (0)