Skip to content

Commit 84f471b

Browse files
authored
[IRGen] Handle ProtocolInfo for protocols whose members aren't used (#18692)
Certain uses of protocols only formally need the requirement signature, not any of the method requirements. This results in IRGen seeing a protocol where none of the members have been validated except the associated types. Account for this by allowing ProtocolInfo to only contain the layout for the base protocols and associated types, if requested. Note that this relies on the layout of a witness table always putting the "requirement signature part" at the front, or at least at offsets that aren't affected by function requirements. rdar://problem/43260117
1 parent ca343ae commit 84f471b

13 files changed

+140
-38
lines changed

include/swift/SIL/SILWitnessVisitor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,20 @@ template <class T> class SILWitnessVisitor : public ASTVisitor<T> {
122122
// Add the associated types if we haven't yet.
123123
addAssociatedTypes();
124124

125+
if (asDerived().shouldVisitRequirementSignatureOnly())
126+
return;
127+
125128
// Visit the witnesses for the direct members of a protocol.
126129
for (Decl *member : protocol->getMembers())
127130
ASTVisitor<T>::visit(member);
128131
}
129132

133+
/// If true, only the base protocols and associated types will be visited.
134+
/// The base implementation returns false.
135+
bool shouldVisitRequirementSignatureOnly() const {
136+
return false;
137+
}
138+
130139
/// Fallback for unexpected protocol requirements.
131140
void visitDecl(Decl *d) {
132141
llvm_unreachable("unhandled protocol requirement");

lib/IRGen/Fulfillment.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ bool FulfillmentMap::searchWitnessTable(
209209

210210
bool hadFulfillment = false;
211211

212-
auto &pi = IGM.getProtocolInfo(protocol);
212+
auto &pi = IGM.getProtocolInfo(protocol,
213+
ProtocolInfoKind::RequirementSignature);
213214

214215
for (auto &entry : pi.getWitnessEntries()) {
215216
if (!entry.isBase()) continue;

lib/IRGen/GenArchetype.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF,
182182
"non-opened archetype lacking generic environment?");
183183
SmallVector<ProtocolEntry, 4> entries;
184184
for (auto p : archetype->getConformsTo()) {
185-
const ProtocolInfo &impl = IGF.IGM.getProtocolInfo(p);
185+
const ProtocolInfo &impl =
186+
IGF.IGM.getProtocolInfo(p, ProtocolInfoKind::RequirementSignature);
186187
entries.push_back(ProtocolEntry(p, impl));
187188
}
188189

@@ -227,7 +228,9 @@ llvm::Value *irgen::emitArchetypeWitnessTableRef(IRGenFunction &IGF,
227228
CanType depType = CanType(entry.first);
228229
ProtocolDecl *requirement = entry.second;
229230

230-
auto &lastPI = IGF.IGM.getProtocolInfo(lastProtocol);
231+
const ProtocolInfo &lastPI =
232+
IGF.IGM.getProtocolInfo(lastProtocol,
233+
ProtocolInfoKind::RequirementSignature);
231234

232235
// If it's a type parameter, it's self, and this is a base protocol
233236
// requirement.

lib/IRGen/GenDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,8 @@ llvm::Constant *IRGenModule::getAddrOfAssociatedTypeGenericParamRef(
920920

921921
// Find the offset of the associated type entry in witness tables of this
922922
// protocol.
923-
auto &protoInfo = getProtocolInfo(assocType->getProtocol());
923+
auto &protoInfo = getProtocolInfo(assocType->getProtocol(),
924+
ProtocolInfoKind::RequirementSignature);
924925
auto index = protoInfo.getAssociatedTypeIndex(AssociatedType(assocType))
925926
.getValue();
926927

lib/IRGen/GenKeyPath.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,8 @@ emitKeyPathComponent(IRGenModule &IGM,
974974
idValue = llvm::ConstantInt::get(IGM.SizeTy, offset.getValue());
975975
idResolved = true;
976976
} else if (auto methodProto = dyn_cast<ProtocolDecl>(dc)) {
977-
auto &protoInfo = IGM.getProtocolInfo(methodProto);
977+
auto &protoInfo = IGM.getProtocolInfo(methodProto,
978+
ProtocolInfoKind::Full);
978979
auto index = protoInfo.getFunctionIndex(
979980
cast<AbstractFunctionDecl>(declRef.getDecl()));
980981
idValue = llvm::ConstantInt::get(IGM.SizeTy, -index.getValue());

lib/IRGen/GenMeta.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ namespace {
654654
}
655655

656656
void addRequirements() {
657-
auto &pi = IGM.getProtocolInfo(Proto);
657+
auto &pi = IGM.getProtocolInfo(Proto, ProtocolInfoKind::Full);
658658

659659
B.fillPlaceholderWithInt(*NumRequirements, IGM.Int32Ty,
660660
pi.getNumWitnesses());
@@ -692,7 +692,8 @@ namespace {
692692
void addAssociatedTypeNames() {
693693
std::string AssociatedTypeNames;
694694

695-
auto &pi = IGM.getProtocolInfo(Proto);
695+
auto &pi = IGM.getProtocolInfo(Proto,
696+
ProtocolInfoKind::RequirementSignature);
696697
for (auto &entry : pi.getWitnessEntries()) {
697698
// Add the associated type name to the list.
698699
if (entry.isAssociatedType()) {

lib/IRGen/GenProto.cpp

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,24 @@ namespace {
749749
/// A class which lays out a witness table in the abstract.
750750
class WitnessTableLayout : public SILWitnessVisitor<WitnessTableLayout> {
751751
SmallVector<WitnessTableEntry, 16> Entries;
752+
bool requirementSignatureOnly;
752753

753754
public:
755+
explicit WitnessTableLayout(ProtocolInfoKind resultKind) {
756+
switch (resultKind) {
757+
case ProtocolInfoKind::RequirementSignature:
758+
requirementSignatureOnly = true;
759+
break;
760+
case ProtocolInfoKind::Full:
761+
requirementSignatureOnly = false;
762+
break;
763+
}
764+
}
765+
766+
bool shouldVisitRequirementSignatureOnly() {
767+
return requirementSignatureOnly;
768+
}
769+
754770
void addProtocolConformanceDescriptor() { }
755771

756772
/// The next witness is an out-of-line base protocol.
@@ -896,9 +912,11 @@ namespace {
896912

897913
// Otherwise, if there isn't a better path through this base,
898914
// don't accumulate anything in the path.
899-
} else if (!findBetterPath(base, IGM.getProtocolInfo(base),
900-
lengthToBase)) {
901-
continue;
915+
} else {
916+
const ProtocolInfo &baseInfo =
917+
IGM.getProtocolInfo(base, ProtocolInfoKind::RequirementSignature);
918+
if (!findBetterPath(base, baseInfo, lengthToBase))
919+
continue;
902920
}
903921

904922
// Okay, we've found a better path, and ReversePath contains a
@@ -1210,7 +1228,8 @@ llvm::Value *uniqueForeignWitnessTableRef(IRGenFunction &IGF,
12101228
Conformance.getDeclContext())),
12111229
SILEntries(SILWT->getEntries()),
12121230
SILConditionalConformances(SILWT->getConditionalConformances()),
1213-
PI(IGM.getProtocolInfo(SILWT->getConformance()->getProtocol())) {
1231+
PI(IGM.getProtocolInfo(SILWT->getConformance()->getProtocol(),
1232+
ProtocolInfoKind::Full)) {
12141233
// If the conformance is resilient, we require runtime instantiation.
12151234
if (isResilientConformance(&Conformance)) {
12161235
RequiresSpecialization = true;
@@ -2079,38 +2098,53 @@ void IRGenModule::ensureRelativeSymbolCollocation(SILWitnessTable &wt) {
20792098
}
20802099

20812100
/// Do a memoized witness-table layout for a protocol.
2082-
const ProtocolInfo &IRGenModule::getProtocolInfo(ProtocolDecl *protocol) {
2083-
return Types.getProtocolInfo(protocol);
2101+
const ProtocolInfo &IRGenModule::getProtocolInfo(ProtocolDecl *protocol,
2102+
ProtocolInfoKind kind) {
2103+
return Types.getProtocolInfo(protocol, kind);
20842104
}
20852105

20862106
/// Do a memoized witness-table layout for a protocol.
2087-
const ProtocolInfo &TypeConverter::getProtocolInfo(ProtocolDecl *protocol) {
2107+
const ProtocolInfo &TypeConverter::getProtocolInfo(ProtocolDecl *protocol,
2108+
ProtocolInfoKind kind) {
20882109
// Check whether we've already translated this protocol.
20892110
auto it = Protocols.find(protocol);
2090-
if (it != Protocols.end()) return *it->second;
2111+
if (it != Protocols.end() && it->second->getKind() >= kind)
2112+
return *it->second;
20912113

20922114
// If not, lay out the protocol's witness table, if it needs one.
2093-
WitnessTableLayout layout;
2115+
WitnessTableLayout layout(kind);
20942116
if (Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
20952117
layout.visitProtocolDecl(protocol);
20962118

20972119
// Create a ProtocolInfo object from the layout.
2098-
ProtocolInfo *info = ProtocolInfo::create(layout.getEntries());
2099-
info->NextConverted = FirstProtocol;
2120+
ProtocolInfo *info = ProtocolInfo::create(layout.getEntries(), kind);
2121+
info->NextConverted.setPointer(FirstProtocol);
21002122
FirstProtocol = info;
21012123

2124+
// Verify that we haven't generated an incompatible layout.
2125+
if (it != Protocols.end()) {
2126+
ArrayRef<WitnessTableEntry> originalEntries =
2127+
it->second->getWitnessEntries();
2128+
ArrayRef<WitnessTableEntry> newEntries = info->getWitnessEntries();
2129+
assert(newEntries.size() >= originalEntries.size());
2130+
assert(newEntries.take_front(originalEntries.size()) == originalEntries);
2131+
(void)originalEntries;
2132+
(void)newEntries;
2133+
}
2134+
21022135
// Memoize.
2103-
Protocols.insert(std::make_pair(protocol, info));
2136+
Protocols[protocol] = info;
21042137

21052138
// Done.
21062139
return *info;
21072140
}
21082141

21092142
/// Allocate a new ProtocolInfo.
2110-
ProtocolInfo *ProtocolInfo::create(ArrayRef<WitnessTableEntry> table) {
2143+
ProtocolInfo *ProtocolInfo::create(ArrayRef<WitnessTableEntry> table,
2144+
ProtocolInfoKind kind) {
21112145
size_t bufferSize = totalSizeToAlloc<WitnessTableEntry>(table.size());
21122146
void *buffer = ::operator new(bufferSize);
2113-
return new(buffer) ProtocolInfo(table);
2147+
return new(buffer) ProtocolInfo(table, kind);
21142148
}
21152149

21162150
// Provide a unique home for the ConformanceInfo vtable.
@@ -2486,7 +2520,8 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF,
24862520
case Component::Kind::OutOfLineBaseProtocol: {
24872521
auto conformance = sourceKey.Kind.getProtocolConformance();
24882522
auto protocol = conformance.getRequirement();
2489-
auto &pi = IGF.IGM.getProtocolInfo(protocol);
2523+
auto &pi = IGF.IGM.getProtocolInfo(protocol,
2524+
ProtocolInfoKind::RequirementSignature);
24902525

24912526
auto &entry = pi.getWitnessEntries()[component.getPrimaryIndex()];
24922527
assert(entry.isOutOfLineBase());
@@ -2522,7 +2557,8 @@ MetadataResponse MetadataPath::followComponent(IRGenFunction &IGF,
25222557
auto sourceType = sourceKey.Type;
25232558
auto sourceConformance = sourceKey.Kind.getProtocolConformance();
25242559
auto sourceProtocol = sourceConformance.getRequirement();
2525-
auto &pi = IGF.IGM.getProtocolInfo(sourceProtocol);
2560+
auto &pi = IGF.IGM.getProtocolInfo(sourceProtocol,
2561+
ProtocolInfoKind::RequirementSignature);
25262562

25272563
auto &entry = pi.getWitnessEntries()[component.getPrimaryIndex()];
25282564
assert(entry.isAssociatedConformance());
@@ -3243,7 +3279,7 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF,
32433279
assert(!IGF.IGM.isResilient(proto, ResilienceExpansion::Maximal));
32443280

32453281
// Find the witness we're interested in.
3246-
auto &fnProtoInfo = IGF.IGM.getProtocolInfo(proto);
3282+
auto &fnProtoInfo = IGF.IGM.getProtocolInfo(proto, ProtocolInfoKind::Full);
32473283
auto index = fnProtoInfo.getFunctionIndex(fn);
32483284
llvm::Value *witnessFnPtr =
32493285
emitInvariantLoadOfOpaqueWitness(IGF, wtable,
@@ -3292,7 +3328,8 @@ irgen::emitAssociatedTypeMetadataRef(IRGenFunction &IGF,
32923328
llvm::Value *wtable,
32933329
AssociatedType associatedType,
32943330
DynamicMetadataRequest request) {
3295-
auto &pi = IGF.IGM.getProtocolInfo(associatedType.getSourceProtocol());
3331+
auto &pi = IGF.IGM.getProtocolInfo(associatedType.getSourceProtocol(),
3332+
ProtocolInfoKind::RequirementSignature);
32963333
auto index = pi.getAssociatedTypeIndex(associatedType);
32973334
llvm::Value *witness = emitInvariantLoadOfOpaqueWitness(IGF, wtable,
32983335
index.forProtocolWitnessTable());

lib/IRGen/GenType.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,12 +1058,10 @@ TypeConverter::createImmovable(llvm::Type *type, Size size, Alignment align) {
10581058
}
10591059

10601060
static TypeInfo *invalidTypeInfo() { return (TypeInfo*) 1; }
1061-
static ProtocolInfo *invalidProtocolInfo() { return (ProtocolInfo*) 1; }
10621061

10631062
TypeConverter::TypeConverter(IRGenModule &IGM)
10641063
: IGM(IGM),
1065-
FirstType(invalidTypeInfo()),
1066-
FirstProtocol(invalidProtocolInfo()) {
1064+
FirstType(invalidTypeInfo()) {
10671065
// FIXME: In LLDB, everything is completely fragile, so that IRGen can query
10681066
// the size of resilient types. Of course this is not the right long term
10691067
// solution, because it won't work once the swiftmodule file is not in
@@ -1081,9 +1079,9 @@ TypeConverter::~TypeConverter() {
10811079
delete Cur;
10821080
}
10831081

1084-
for (const ProtocolInfo *I = FirstProtocol; I != invalidProtocolInfo(); ) {
1082+
for (const ProtocolInfo *I = FirstProtocol; I != nullptr; ) {
10851083
const ProtocolInfo *Cur = I;
1086-
I = Cur->NextConverted;
1084+
I = Cur->NextConverted.getPointer();
10871085
delete Cur;
10881086
}
10891087
}

lib/IRGen/GenType.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class TypeConverter {
7070
llvm::DenseMap<ProtocolDecl*, const ProtocolInfo*> Protocols;
7171
const TypeInfo *FirstType;
7272

73-
const ProtocolInfo *FirstProtocol;
73+
const ProtocolInfo *FirstProtocol = nullptr;
7474
const LoadableTypeInfo *NativeObjectTI = nullptr;
7575
const LoadableTypeInfo *UnknownObjectTI = nullptr;
7676
const LoadableTypeInfo *BridgeObjectTI = nullptr;
@@ -146,7 +146,7 @@ class TypeConverter {
146146
const LoadableTypeInfo &getWitnessTablePtrTypeInfo();
147147
const LoadableTypeInfo &getEmptyTypeInfo();
148148
const TypeInfo &getResilientStructTypeInfo(IsABIAccessible_t abiAccessible);
149-
const ProtocolInfo &getProtocolInfo(ProtocolDecl *P);
149+
const ProtocolInfo &getProtocolInfo(ProtocolDecl *P, ProtocolInfoKind kind);
150150
const LoadableTypeInfo &getOpaqueStorageTypeInfo(Size storageSize,
151151
Alignment storageAlign);
152152
const TypeInfo &getMetatypeTypeInfo(MetatypeRepresentation representation);

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ namespace irgen {
129129
class NominalMetadataLayout;
130130
class OutliningMetadataCollector;
131131
class ProtocolInfo;
132+
enum class ProtocolInfoKind : uint8_t;
132133
class Signature;
133134
class StructMetadataLayout;
134135
struct SymbolicMangling;
@@ -703,7 +704,7 @@ class IRGenModule {
703704

704705
//--- Types -----------------------------------------------------------------
705706
public:
706-
const ProtocolInfo &getProtocolInfo(ProtocolDecl *D);
707+
const ProtocolInfo &getProtocolInfo(ProtocolDecl *D, ProtocolInfoKind kind);
707708

708709
// Not strictly a type operation, but similar.
709710
const ConformanceInfo &

lib/IRGen/ProtocolInfo.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,19 @@ class WitnessTableEntry {
146146
assert(isAssociatedConformance());
147147
return Protocol;
148148
}
149+
150+
friend bool operator==(WitnessTableEntry left, WitnessTableEntry right) {
151+
return left.MemberOrAssociatedType == right.MemberOrAssociatedType &&
152+
left.Protocol == right.Protocol;
153+
}
154+
};
155+
156+
/// Describes the information available in a ProtocolInfo.
157+
///
158+
/// Each kind includes the information of the kinds before it.
159+
enum class ProtocolInfoKind : uint8_t {
160+
RequirementSignature,
161+
Full
149162
};
150163

151164
/// An abstract description of a protocol.
@@ -154,24 +167,30 @@ class ProtocolInfo final :
154167
friend TrailingObjects;
155168

156169
/// A singly-linked-list of all the protocols that have been laid out.
157-
const ProtocolInfo *NextConverted;
170+
llvm::PointerIntPair<const ProtocolInfo *, 1, ProtocolInfoKind> NextConverted;
158171
friend class TypeConverter;
159172

173+
ProtocolInfoKind getKind() const {
174+
return NextConverted.getInt();
175+
}
176+
160177
/// The number of table entries in this protocol layout.
161178
unsigned NumTableEntries;
162179

163-
ProtocolInfo(ArrayRef<WitnessTableEntry> table)
164-
: NumTableEntries(table.size()) {
180+
ProtocolInfo(ArrayRef<WitnessTableEntry> table, ProtocolInfoKind kind)
181+
: NextConverted(nullptr, kind), NumTableEntries(table.size()) {
165182
std::uninitialized_copy(table.begin(), table.end(),
166183
getTrailingObjects<WitnessTableEntry>());
167184
}
168185

169-
static ProtocolInfo *create(ArrayRef<WitnessTableEntry> table);
186+
static ProtocolInfo *create(ArrayRef<WitnessTableEntry> table,
187+
ProtocolInfoKind kind);
170188

171189
public:
172190
/// The number of witness slots in a conformance to this protocol;
173191
/// in other words, the size of the table in words.
174192
unsigned getNumWitnesses() const {
193+
assert(getKind() == ProtocolInfoKind::Full);
175194
return NumTableEntries;
176195
}
177196

@@ -219,6 +238,7 @@ class ProtocolInfo final :
219238
/// Return the witness index for the witness function for the given
220239
/// function requirement.
221240
WitnessIndex getFunctionIndex(AbstractFunctionDecl *function) const {
241+
assert(getKind() >= ProtocolInfoKind::Full);
222242
for (auto &witness : getWitnessEntries()) {
223243
if (witness.matchesFunction(function))
224244
return getNonBaseWitnessIndex(&witness);

test/IRGen/Inputs/protocol_accessor_multifile_other.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,13 @@ extension Proto {
88
var globalExistential: Proto {
99
fatalError()
1010
}
11+
12+
protocol ClassProtoBase: AnyObject {
13+
var baseProp: Int { get set }
14+
}
15+
protocol ClassProto: ClassProtoBase {
16+
var prop: Int { get set }
17+
}
18+
func getClassExistential() -> ClassProto? {
19+
fatalError()
20+
}

0 commit comments

Comments
 (0)