Skip to content

Commit 7ef1696

Browse files
authored
Merge pull request #19210 from DougGregor/keypath-protocol-overrides
2 parents 6f6be1e + 282d6cc commit 7ef1696

File tree

10 files changed

+148
-21
lines changed

10 files changed

+148
-21
lines changed

include/swift/SIL/SILDeclRef.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ struct SILDeclRef {
359359
/// entry overridden by this method.
360360
SILDeclRef getOverriddenWitnessTableEntry() const;
361361

362+
/// Return the original protocol requirement that introduced the witness table
363+
/// entry overridden by this method.
364+
static AbstractFunctionDecl *getOverriddenWitnessTableEntry(
365+
AbstractFunctionDecl *func);
366+
362367
/// True if the referenced entity is some kind of thunk.
363368
bool isThunk() const;
364369

lib/IRGen/GenKeyPath.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,8 @@ emitKeyPathComponent(IRGenModule &IGM,
940940
} else {
941941
if (auto overridden = declRef.getOverriddenVTableEntry())
942942
declRef = overridden;
943+
if (auto overridden = declRef.getOverriddenWitnessTableEntry())
944+
declRef = overridden;
943945

944946
auto dc = declRef.getDecl()->getDeclContext();
945947

lib/IRGen/GenThunk.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ llvm::Constant *
126126
IRGenModule::getAddrOfMethodDescriptor(SILDeclRef declRef,
127127
ForDefinition_t forDefinition) {
128128
assert(forDefinition == NotForDefinition);
129+
assert(declRef.getOverriddenWitnessTableEntry() == declRef &&
130+
"Overriding protocol requirements do not have method descriptors");
129131
LinkEntity entity = LinkEntity::forMethodDescriptor(declRef);
130132
return getAddrOfLLVMVariable(entity, Alignment(4), forDefinition,
131133
MethodDescriptorStructTy, DebugTypeInfo());

lib/SIL/SILDeclRef.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -782,12 +782,22 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const {
782782
}
783783

784784
SILDeclRef SILDeclRef::getOverriddenWitnessTableEntry() const {
785-
ValueDecl *bestOverridden = nullptr;
785+
auto bestOverridden =
786+
getOverriddenWitnessTableEntry(cast<AbstractFunctionDecl>(getDecl()));
787+
return SILDeclRef(bestOverridden, kind, isCurried);
788+
}
789+
790+
AbstractFunctionDecl *SILDeclRef::getOverriddenWitnessTableEntry(
791+
AbstractFunctionDecl *func) {
792+
if (!isa<ProtocolDecl>(func->getDeclContext()))
793+
return func;
786794

787-
SmallVector<ValueDecl *, 4> stack;
788-
SmallPtrSet<ValueDecl *, 4> visited;
789-
stack.push_back(getDecl());
790-
visited.insert(getDecl());
795+
AbstractFunctionDecl *bestOverridden = nullptr;
796+
797+
SmallVector<AbstractFunctionDecl *, 4> stack;
798+
SmallPtrSet<AbstractFunctionDecl *, 4> visited;
799+
stack.push_back(func);
800+
visited.insert(func);
791801

792802
while (!stack.empty()) {
793803
auto current = stack.back();
@@ -802,20 +812,21 @@ SILDeclRef SILDeclRef::getOverriddenWitnessTableEntry() const {
802812
cast<ProtocolDecl>(current->getDeclContext()),
803813
cast<ProtocolDecl>(bestOverridden->getDeclContext()))
804814
< 0) {
805-
bestOverridden = current;
815+
bestOverridden = cast<AbstractFunctionDecl>(current);
806816
}
807817

808818
continue;
809819
}
810820

811821
// Add overridden declarations to the stack.
812822
for (auto overridden : overriddenDecls) {
813-
if (visited.insert(overridden).second)
814-
stack.push_back(overridden);
823+
auto overriddenFunc = cast<AbstractFunctionDecl>(overridden);
824+
if (visited.insert(overriddenFunc).second)
825+
stack.push_back(overriddenFunc);
815826
}
816827
}
817828

818-
return SILDeclRef(bestOverridden, kind, isCurried);
829+
return bestOverridden;
819830
}
820831

821832
SILDeclRef SILDeclRef::getOverriddenVTableEntry() const {

lib/SILGen/SILGen.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
442442
SubstitutionMap
443443
getNonMemberVarDeclSubstitutions(VarDecl *var);
444444

445+
/// Map the substitutions for the original declaration to substitutions for
446+
/// the overridden declaration.
447+
static SubstitutionMap mapSubstitutionsForWitnessOverride(
448+
AbstractFunctionDecl *original,
449+
AbstractFunctionDecl *overridden,
450+
SubstitutionMap subs);
451+
445452
/// Emit a property descriptor for the given storage decl if it needs one.
446453
void tryEmitPropertyDescriptor(AbstractStorageDecl *decl);
447454

lib/SILGen/SILGenApply.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,21 @@ using namespace Lowering;
4444
// Utility Functions
4545
//===----------------------------------------------------------------------===//
4646

47+
SubstitutionMap SILGenModule::mapSubstitutionsForWitnessOverride(
48+
AbstractFunctionDecl *original,
49+
AbstractFunctionDecl *overridden,
50+
SubstitutionMap subs) {
51+
// Substitute the 'Self' type of the base protocol.
52+
auto origProto = cast<ProtocolDecl>(original->getDeclContext());
53+
Type origProtoSelfType = origProto->getProtocolSelfType();
54+
auto baseProto = cast<ProtocolDecl>(overridden->getDeclContext());
55+
return SubstitutionMap::getProtocolSubstitutions(
56+
baseProto,
57+
origProtoSelfType.subst(subs),
58+
*subs.lookupConformance(origProtoSelfType->getCanonicalType(),
59+
baseProto));
60+
}
61+
4762
/// Return the abstraction pattern to use when calling a function value.
4863
static AbstractionPattern
4964
getIndirectApplyAbstractionPattern(SILGenFunction &SGF,
@@ -390,23 +405,16 @@ class Callee {
390405
SILLocation l) {
391406
// Find a witness that has an entry in the witness table.
392407
if (!c.requiresNewWitnessTableEntry()) {
393-
auto origProto =
394-
cast<ProtocolDecl>(c.getDecl()->getDeclContext());
395-
396408
// Retrieve the constant that has an entry in the witness table.
409+
auto original = cast<AbstractFunctionDecl>(c.getDecl());
397410
c = c.getOverriddenWitnessTableEntry();
398411
c = c.asForeign(c.getDecl()->isObjC());
412+
auto overridden = cast<AbstractFunctionDecl>(c.getDecl());
399413

400414
// Substitute the 'Self' type of the base protocol.
401-
auto baseProto = cast<ProtocolDecl>(c.getDecl()->getDeclContext());
402-
Type origProtoSelfType = origProto->getProtocolSelfType();
403-
auto baseProtoSubs =
404-
SubstitutionMap::getProtocolSubstitutions(
405-
baseProto,
406-
origProtoSelfType.subst(subs),
407-
*subs.lookupConformance(origProtoSelfType->getCanonicalType(),
408-
baseProto));
409-
subs = baseProtoSubs;
415+
subs = SILGenModule::mapSubstitutionsForWitnessOverride(original,
416+
overridden,
417+
subs);
410418
}
411419

412420
auto &ci = SGF.getConstantInfo(c);

lib/SILGen/SILGenExpr.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2852,6 +2852,23 @@ static SILFunction *getOrCreateKeyPathGetter(SILGenModule &SGM,
28522852
ArrayRef<IndexTypePair> indexes,
28532853
CanType baseType,
28542854
CanType propertyType) {
2855+
// If the storage declaration is from a protocol, chase the override chain
2856+
// back to the declaration whose getter introduced the witness table
2857+
// entry.
2858+
if (auto origProto = dyn_cast<ProtocolDecl>(property->getDeclContext())) {
2859+
auto getter = property->getGetter();
2860+
if (!SILDeclRef::requiresNewWitnessTableEntry(getter)) {
2861+
// Find the getter that does have a witness table entry.
2862+
auto wtableGetter =
2863+
cast<AccessorDecl>(SILDeclRef::getOverriddenWitnessTableEntry(getter));
2864+
2865+
// Substitute the 'Self' type of the base protocol.
2866+
subs = SILGenModule::mapSubstitutionsForWitnessOverride(
2867+
getter, wtableGetter, subs);
2868+
property = wtableGetter->getStorage();
2869+
}
2870+
}
2871+
28552872
auto genericSig = genericEnv
28562873
? genericEnv->getGenericSignature()->getCanonicalSignature()
28572874
: nullptr;
@@ -2966,6 +2983,23 @@ static SILFunction *getOrCreateKeyPathSetter(SILGenModule &SGM,
29662983
ArrayRef<IndexTypePair> indexes,
29672984
CanType baseType,
29682985
CanType propertyType) {
2986+
// If the storage declaration is from a protocol, chase the override chain
2987+
// back to the declaration whose setter introduced the witness table
2988+
// entry.
2989+
if (auto origProto = dyn_cast<ProtocolDecl>(property->getDeclContext())) {
2990+
auto setter = property->getSetter();
2991+
if (!SILDeclRef::requiresNewWitnessTableEntry(setter)) {
2992+
// Find the setter that does have a witness table entry.
2993+
auto wtableSetter =
2994+
cast<AccessorDecl>(SILDeclRef::getOverriddenWitnessTableEntry(setter));
2995+
2996+
// Substitute the 'Self' type of the base protocol.
2997+
subs = SILGenModule::mapSubstitutionsForWitnessOverride(
2998+
setter, wtableSetter, subs);
2999+
property = wtableSetter->getStorage();
3000+
}
3001+
}
3002+
29693003
auto genericSig = genericEnv
29703004
? genericEnv->getGenericSignature()->getCanonicalSignature()
29713005
: nullptr;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -module-name protocol_overrides -emit-module -enable-sil-ownership -enable-resilience -emit-module-path=%t/protocol_overrides.swiftmodule %S/../SILGen/Inputs/protocol_overrides.swift
3+
// RUN: %target-swift-frontend -module-name keypath_witness_overrides -emit-ir %s -I %t | %FileCheck %s
4+
5+
import protocol_overrides
6+
7+
// CHECK: @keypath = private global
8+
// CHECK-SAME: %swift.method_descriptor* @"$S18protocol_overrides14OriginalGetterPy7ElementQz5IndexQzcigTq"
9+
public func getWritableKeyPath<OS: OverridesSetter>(_ c: OS, index: OS.Index) -> AnyKeyPath
10+
where OS.Index: Hashable {
11+
let keypath = \OS.[index]
12+
return keypath
13+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
public protocol OriginalGetter {
2+
associatedtype Index
3+
associatedtype Element
4+
5+
subscript (index: Index) -> Element { get }
6+
}
7+
8+
public protocol OverridesGetter: OriginalGetter {
9+
override subscript (index: Index) -> Element { get }
10+
}
11+
12+
public protocol AddsSetter: OverridesGetter {
13+
override subscript (index: Index) -> Element { get set }
14+
}
15+
16+
public protocol OverridesSetter: AddsSetter {
17+
override subscript (index: Index) -> Element { get set }
18+
}
19+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -module-name protocol_overrides -emit-module -enable-sil-ownership -enable-resilience -emit-module-path=%t/protocol_overrides.swiftmodule %S/Inputs/protocol_overrides.swift
3+
// RUN: %target-swift-emit-silgen %s -I %t | %FileCheck %s
4+
5+
// Check that keypath formation properly records the point at which the witness
6+
// table entry for a protocol requirement is introduced.
7+
import protocol_overrides
8+
9+
// CHECK-LABEL: sil hidden @$S25keypath_witness_overrides18getWritableKeyPath_5indexs03AnyfG0Cx_5IndexQzt09protocol_C015OverridesSetterRzSHAGRQlF
10+
func getWritableKeyPath<OS: OverridesSetter>(_ c: OS, index: OS.Index) -> AnyKeyPath
11+
where OS.Index: Hashable {
12+
// CHECK: keypath $WritableKeyPath<OS, OS.Element>,
13+
// CHECK-SAME: id #OverridesSetter.subscript!getter.1
14+
// CHECK-SAME: getter @$S18protocol_overrides14OriginalGetterPy7ElementQz5IndexQzcipAA15OverridesSetterRzSHAGRQlxxTK
15+
// CHECK-SAME: setter @$S18protocol_overrides10AddsSetterPy7ElementQz5IndexQzcipAA09OverridesD0RzSHAGRQlxxTk
16+
// CHECK-SAME: indices_equals @$S5Index18protocol_overrides14OriginalGetterPQzAB15OverridesSetterRzSHAERQlTH
17+
// CHECK-SAME: indices_hash @$S5Index18protocol_overrides14OriginalGetterPQzAB15OverridesSetterRzSHAERQlTh
18+
let keypath = \OS.[index]
19+
return keypath
20+
}
21+
22+
// CHECK-LABEL: sil shared [thunk] @$S18protocol_overrides14OriginalGetterPy7ElementQz5IndexQzcipAA15OverridesSetterRzSHAGRQlxxTK
23+
// CHECK: witness_method $OS, #OriginalGetter.subscript!getter.1
24+
25+
// CHECK-LABEL: sil shared [thunk] @$S18protocol_overrides10AddsSetterPy7ElementQz5IndexQzcipAA09OverridesD0RzSHAGRQlxxTk
26+
// CHECK-LABEL: witness_method $OS, #AddsSetter.subscript!setter.1

0 commit comments

Comments
 (0)