Skip to content

Commit 44d8720

Browse files
authored
Merge pull request #13089 from DougGregor/conditional-conformances-sr-6478
2 parents 6b611a6 + 7f17f44 commit 44d8720

File tree

7 files changed

+166
-45
lines changed

7 files changed

+166
-45
lines changed

lib/IRGen/GenOpaque.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace irgen {
2929
class IRGenFunction;
3030
class IRGenModule;
3131
enum class ValueWitness : unsigned;
32+
class StackAddress;
3233
class WitnessIndex;
3334

3435
/// Return the size of a fixed buffer.

lib/IRGen/GenProto.cpp

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "swift/AST/CanTypeVisitor.h"
3232
#include "swift/AST/Types.h"
3333
#include "swift/AST/Decl.h"
34+
#include "swift/AST/GenericEnvironment.h"
3435
#include "swift/AST/IRGenOptions.h"
3536
#include "swift/AST/SubstitutionMap.h"
3637
#include "swift/ClangImporter/ClangModule.h"
@@ -74,10 +75,6 @@
7475
using namespace swift;
7576
using namespace irgen;
7677

77-
// Return the offset one should do on a witness table pointer to retrieve the
78-
// `index`th piece of private data.
79-
static int privateIndexToTableOffset(unsigned index) { return -1 - (int)index; }
80-
8178
namespace {
8279

8380
/// A class for computing how to pass arguments to a polymorphic
@@ -574,30 +571,12 @@ void EmitPolymorphicParameters::bindExtraSource(const MetadataSource &source,
574571
}
575572

576573
if (conformance.isConcrete()) {
577-
// Now bind all the conditional witness tables that can be pulled out of
578-
// the self witness table.
579-
SILWitnessTable::enumerateWitnessTableConditionalConformances(
580-
conformance.getConcrete(),
581-
[&](unsigned index, CanType type, ProtocolDecl *proto) {
582-
auto archetype = getTypeInContext(type);
583-
if (isa<ArchetypeType>(archetype)) {
584-
WitnessIndex wIndex(privateIndexToTableOffset(index),
585-
/*prefix*/ false);
586-
587-
auto table =
588-
emitInvariantLoadOfOpaqueWitness(IGF, selfTable, wIndex);
589-
table =
590-
IGF.Builder.CreateBitCast(table, IGF.IGM.WitnessTablePtrTy);
591-
setProtocolWitnessTableName(IGF.IGM, table, archetype, proto);
592-
593-
IGF.setUnscopedLocalTypeData(
594-
archetype,
595-
LocalTypeDataKind::forAbstractProtocolWitnessTable(proto),
596-
table);
597-
}
598-
599-
return /*finished?*/ false;
600-
});
574+
IGF.bindLocalTypeDataFromSelfWitnessTable(
575+
conformance.getConcrete(),
576+
selfTable,
577+
[this](CanType type) {
578+
return getTypeInContext(type);
579+
});
601580
}
602581
return;
603582
}
@@ -1017,7 +996,7 @@ class irgen::ConformanceInfo {
1017996
};
1018997

1019998
static std::pair<llvm::Value *, llvm::Value *>
1020-
emitConditionalConformancesBuffer(IRGenFunction &IGF,
999+
emitConditionalConformancesBuffer(IRGenFunction &IGF, CanType conformingType,
10211000
const ProtocolConformance *conformance) {
10221001
// Pointers to the witness tables, in the right order, which will be included
10231002
// in the buffer that gets passed to the witness table accessor.
@@ -1026,6 +1005,30 @@ emitConditionalConformancesBuffer(IRGenFunction &IGF,
10261005
auto subMap = conformance->getSubstitutions(IGF.IGM.getSwiftModule());
10271006
auto rootConformance = conformance->getRootNormalConformance();
10281007

1008+
// Find the generic environment into which the witness table should be
1009+
// mapped.
1010+
// FIXME: Passing conformingType down for just this purpose feels like a
1011+
// hack.
1012+
if (conformingType->hasArchetype() &&
1013+
conformance->getType()->hasTypeParameter()) {
1014+
GenericEnvironment *conformingTypeEnv = nullptr;
1015+
conformingType.findIf([&](Type type) {
1016+
if (auto archetype = type->getAs<ArchetypeType>()) {
1017+
conformingTypeEnv = archetype->getGenericEnvironment();
1018+
return conformingTypeEnv != nullptr;
1019+
}
1020+
1021+
return false;
1022+
});
1023+
1024+
if (conformingTypeEnv) {
1025+
subMap = subMap.subst([&](SubstitutableType *dependentType) {
1026+
return conformingTypeEnv->mapTypeIntoContext(Type(dependentType));
1027+
},
1028+
LookUpConformanceInModule(IGF.getSwiftModule()));
1029+
}
1030+
}
1031+
10291032
SILWitnessTable::enumerateWitnessTableConditionalConformances(
10301033
rootConformance, [&](unsigned, CanType type, ProtocolDecl *proto) {
10311034
auto substType = type.subst(subMap)->getCanonicalType();
@@ -1078,7 +1081,7 @@ static llvm::Value *emitWitnessTableAccessorCall(
10781081

10791082
llvm::Value *conditionalTables, *numConditionalTables;
10801083
std::tie(conditionalTables, numConditionalTables) =
1081-
emitConditionalConformancesBuffer(IGF, conformance);
1084+
emitConditionalConformancesBuffer(IGF, conformingType, conformance);
10821085

10831086
call = IGF.Builder.CreateCall(
10841087
accessor, {*srcMetadataCache, conditionalTables, numConditionalTables});
@@ -1414,7 +1417,8 @@ class AccessorConformanceInfo : public ConformanceInfo {
14141417
unsigned index) {
14151418
assert(index < NextPrivateDataIndex);
14161419
return IGF.Builder.CreateConstArrayGEP(
1417-
table, privateIndexToTableOffset(index), IGF.IGM.getPointerSize());
1420+
table, privateWitnessTableIndexToTableOffset(index),
1421+
IGF.IGM.getPointerSize());
14181422
}
14191423

14201424
const FulfillmentMap &getFulfillmentMap() {
@@ -1488,6 +1492,13 @@ getAssociatedTypeMetadataAccessFunction(AssociatedType requirement,
14881492
Address destTable(parameters.claimNext(), IGM.getPointerAlignment());
14891493
setProtocolWitnessTableName(IGM, destTable.getAddress(), ConcreteType,
14901494
requirement.getSourceProtocol());
1495+
IGF.bindLocalTypeDataFromSelfWitnessTable(
1496+
&Conformance,
1497+
destTable.getAddress(),
1498+
[&](CanType type) {
1499+
return Conformance.getDeclContext()->mapTypeIntoContext(type)
1500+
->getCanonicalType();
1501+
});
14911502

14921503
// If the associated type is directly fulfillable from the type,
14931504
// we don't need a cache entry.
@@ -1541,7 +1552,7 @@ getOrCreateWitnessTableAccessFunction(IRGenModule &IGM, CanType type,
15411552
// function.
15421553
auto rootConformance = conformance->getRootNormalConformance();
15431554
if (rootConformance->witnessTableAccessorRequiresArguments()) {
1544-
return getWitnessTableLazyAccessFunction(IGM, rootConformance, type);
1555+
return getWitnessTableLazyAccessFunction(IGM, conformance, type);
15451556
} else {
15461557
return IGM.getAddrOfWitnessTableAccessFunction(rootConformance,
15471558
NotForDefinition);
@@ -1601,6 +1612,13 @@ getAssociatedTypeWitnessTableAccessFunction(AssociatedConformance requirement,
16011612
Address destTable(parameters.claimNext(), IGM.getPointerAlignment());
16021613
setProtocolWitnessTableName(IGM, destTable.getAddress(), ConcreteType,
16031614
Conformance.getProtocol());
1615+
IGF.bindLocalTypeDataFromSelfWitnessTable(
1616+
&Conformance,
1617+
destTable.getAddress(),
1618+
[&](CanType type) {
1619+
return Conformance.getDeclContext()->mapTypeIntoContext(type)
1620+
->getCanonicalType();
1621+
});
16041622

16051623
ProtocolDecl *associatedProtocol = requirement.getAssociatedRequirement();
16061624

@@ -1894,6 +1912,33 @@ llvm::Constant *WitnessTableBuilder::buildInstantiationFunction() {
18941912
// All good: now we can actually fill in the witness table.
18951913
IGF.Builder.emitBlock(contBB);
18961914

1915+
/// Run through the conditional conformance witness tables, pulling them out
1916+
/// of the slice and putting them into the private data of the witness table.
1917+
for (auto idx : indices(ConditionalRequirementPrivateDataIndices)) {
1918+
Address conditionalTablePtr =
1919+
IGF.Builder.CreateConstArrayGEP(conditionalTables, idx, PointerSize);
1920+
Address slot = getAddressOfPrivateDataSlot(
1921+
IGF, wtable, ConditionalRequirementPrivateDataIndices[idx]);
1922+
auto conditionalTable = IGF.Builder.CreateLoad(conditionalTablePtr);
1923+
auto coercedSlot =
1924+
IGF.Builder.CreateElementBitCast(slot, conditionalTable->getType());
1925+
IGF.Builder.CreateStore(conditionalTable, coercedSlot);
1926+
1927+
// Register local type data for the conditional conformance witness table.
1928+
const auto &condConformance = SILConditionalConformances[idx];
1929+
CanType reqTypeInContext =
1930+
Conformance.getDeclContext()
1931+
->mapTypeIntoContext(condConformance.Requirement)
1932+
->getCanonicalType();
1933+
if (auto archetype = dyn_cast<ArchetypeType>(reqTypeInContext)) {
1934+
auto condProto = condConformance.Conformance.getRequirement();
1935+
IGF.setUnscopedLocalTypeData(
1936+
archetype,
1937+
LocalTypeDataKind::forAbstractProtocolWitnessTable(condProto),
1938+
conditionalTable);
1939+
}
1940+
}
1941+
18971942
// Initialize all the specialized base conformances.
18981943
for (auto &base : SpecializedBaseConformances) {
18991944
// Ask the ConformanceInfo to emit the wtable.
@@ -1907,18 +1952,6 @@ llvm::Constant *WitnessTableBuilder::buildInstantiationFunction() {
19071952
IGF.Builder.CreateStore(baseWTable, slot);
19081953
}
19091954

1910-
/// Run through the conditional conformance witness tables, pulling them out
1911-
/// of the slice and putting them into the private data of the witness table.
1912-
for (auto idx : indices(ConditionalRequirementPrivateDataIndices)) {
1913-
Address conditionalTablePtr =
1914-
IGF.Builder.CreateConstArrayGEP(conditionalTables, idx, PointerSize);
1915-
Address slot = getAddressOfPrivateDataSlot(
1916-
IGF, wtable, ConditionalRequirementPrivateDataIndices[idx]);
1917-
auto conditionalTable = IGF.Builder.CreateLoad(conditionalTablePtr);
1918-
auto coercedSlot =
1919-
IGF.Builder.CreateElementBitCast(slot, conditionalTable->getType());
1920-
IGF.Builder.CreateStore(conditionalTable, coercedSlot);
1921-
}
19221955

19231956
IGF.Builder.CreateRetVoid();
19241957

@@ -2412,7 +2445,7 @@ llvm::Value *MetadataPath::followComponent(IRGenFunction &IGF,
24122445
LocalTypeDataKind::forAbstractProtocolWitnessTable(conformingProto);
24132446

24142447
if (source) {
2415-
WitnessIndex index(privateIndexToTableOffset(reqtIndex),
2448+
WitnessIndex index(privateWitnessTableIndexToTableOffset(reqtIndex),
24162449
/*prefix*/ false);
24172450

24182451
source = emitInvariantLoadOfOpaqueWitness(IGF, source, index);

lib/IRGen/GenProto.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ namespace irgen {
7373
llvm::Value *wtable,
7474
AssociatedType associatedType);
7575

76+
// Return the offset one should do on a witness table pointer to retrieve the
77+
// `index`th piece of private data.
78+
inline int privateWitnessTableIndexToTableOffset(unsigned index) {
79+
return -1 - (int)index;
80+
}
81+
7682
/// Add the witness parameters necessary for calling a function with
7783
/// the given generics clause.
7884
void expandPolymorphicSignature(IRGenModule &IGM,

lib/IRGen/IRGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,13 @@ class IRGenFunction {
472472
void bindLocalTypeDataFromTypeMetadata(CanType type, IsExact_t isExact,
473473
llvm::Value *metadata);
474474

475+
/// Given the witness table parameter, bind local type data for
476+
/// the witness table itself and any conditional requirements.
477+
void bindLocalTypeDataFromSelfWitnessTable(
478+
const ProtocolConformance *conformance,
479+
llvm::Value *selfTable,
480+
llvm::function_ref<CanType (CanType)> mapTypeIntoContext);
481+
475482
void setDominanceResolver(DominanceResolverFunction resolver) {
476483
assert(DominanceResolver == nullptr);
477484
DominanceResolver = resolver;

lib/IRGen/LocalTypeData.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "LocalTypeData.h"
1919
#include "Fulfillment.h"
2020
#include "GenMeta.h"
21+
#include "GenOpaque.h"
2122
#include "GenProto.h"
2223
#include "IRGenDebugInfo.h"
2324
#include "IRGenFunction.h"
@@ -250,6 +251,33 @@ void IRGenFunction::bindLocalTypeDataFromTypeMetadata(CanType type,
250251
.addAbstractForTypeMetadata(*this, type, isExact, metadata);
251252
}
252253

254+
void IRGenFunction::bindLocalTypeDataFromSelfWitnessTable(
255+
const ProtocolConformance *conformance,
256+
llvm::Value *selfTable,
257+
llvm::function_ref<CanType (CanType)> getTypeInContext) {
258+
SILWitnessTable::enumerateWitnessTableConditionalConformances(
259+
conformance,
260+
[&](unsigned index, CanType type, ProtocolDecl *proto) {
261+
auto archetype = getTypeInContext(type);
262+
if (isa<ArchetypeType>(archetype)) {
263+
WitnessIndex wIndex(privateWitnessTableIndexToTableOffset(index),
264+
/*prefix*/ false);
265+
266+
auto table =
267+
emitInvariantLoadOfOpaqueWitness(*this, selfTable, wIndex);
268+
table = Builder.CreateBitCast(table, IGM.WitnessTablePtrTy);
269+
setProtocolWitnessTableName(IGM, table, archetype, proto);
270+
271+
setUnscopedLocalTypeData(
272+
archetype,
273+
LocalTypeDataKind::forAbstractProtocolWitnessTable(proto),
274+
table);
275+
}
276+
277+
return /*finished?*/ false;
278+
});
279+
}
280+
253281
void LocalTypeDataCache::addAbstractForTypeMetadata(IRGenFunction &IGF,
254282
CanType type,
255283
IsExact_t isExact,

test/IRGen/conditional_conformances.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_basic_conformances.swift | %FileCheck %S/../Inputs/conditional_conformance_basic_conformances.swift
22
// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_with_assoc.swift | %FileCheck %S/../Inputs/conditional_conformance_with_assoc.swift
33
// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_subclass.swift | %FileCheck %S/../Inputs/conditional_conformance_subclass.swift
4+
// RUN: %target-swift-frontend -emit-ir %S/../Inputs/conditional_conformance_recursive.swift | %FileCheck %S/../Inputs/conditional_conformance_recursive.swift
45

56
// Too many pointer-sized integers in the IR
67
// REQUIRES: PTRSIZE=64
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
protocol P1 {
2+
associatedtype A: P1
3+
}
4+
5+
protocol P2: P1 where A: P2 {
6+
associatedtype B
7+
}
8+
9+
struct Wrapper<T: P1>: P1 {
10+
typealias A = Wrapper<T>
11+
}
12+
13+
extension Wrapper: P2 where T: P2 {
14+
typealias B = T.A
15+
}
16+
17+
protocol P3: P2 where A: P3 { }
18+
19+
extension Wrapper: P3 where T: P3 { }
20+
21+
// instantiation function for Wrapper<T>: P3
22+
// CHECK-LABEL: define internal void @_T033conditional_conformance_recursive7WrapperVyxGAA2P3A2aERzrlWI
23+
// CHECK-NOT: ret
24+
// CHECK: call i8** @_T033conditional_conformance_recursive7WrapperVyxGAA2P2A2aERzrlWa
25+
26+
// associated type metadata accessor for B in Wrapper<T>: P2
27+
// CHECK-LABEL: define internal %swift.type* @_T033conditional_conformance_recursive7WrapperVyxGAA2P2A2aERzrl1BWt
28+
// CHECK: [[T_TO_P2_PTR:%.*]] = getelementptr inbounds i8*, i8** [[WRAPPER_T_TO_P2:%.*]], i32 -1
29+
// CHECK: [[T_TO_P2_VAL:%.*]] = load i8*, i8** [[T_TO_P2_PTR]]
30+
// CHECK: [[T_TO_P2:%.*]] = bitcast i8* [[T_TO_P2_VAL]] to i8**
31+
// CHECK: [[T_TO_P1_VAL:%.*]] = load i8*, i8** [[T_TO_P2]]
32+
// CHECK: [[T_TO_P1:%.*]] = bitcast i8* [[T_TO_P1_VAL]] to i8**
33+
// CHECK: [[WRAPPER_T_TYPE:%.*]] = bitcast %swift.type* [[WRAPPER_T:%.*]] to %swift.type**
34+
// CHECK: [[T_TYPE_PTR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[WRAPPER_T_TYPE]], i64 2
35+
// CHECK: [[T_TYPE:%.*]] = load %swift.type*, %swift.type** [[T_TYPE_PTR]]
36+
// CHECK: [[T_B_TYPE_ACCESSOR_PTR:%.*]] = load i8*, i8** [[T_TO_P1]], align 8
37+
// CHECK: [[T_B_TYPE_ACCESSOR:%.*]] = bitcast i8* [[T_B_TYPE_ACCESSOR_PTR]] to %swift.type* (%swift.type*, i8**)*
38+
// CHECK: [[T_A_TYPE:%.*]] = call %swift.type* [[T_B_TYPE_ACCESSOR]](%swift.type* [[T_TYPE]], i8** [[T_TO_P1]])
39+
// CHECK: ret %swift.type* [[T_A_TYPE]]
40+
41+
// associated type witness table accessor for A : P2 in Wrapper<T>: P2
42+
// CHECK-LABEL: define internal i8** @_T033conditional_conformance_recursive7WrapperVyxGAA2P2A2aERzrl1A_AaEPWT
43+
// CHECK: [[CONDITIONAL_REQ_BUFFER:%.*]] = alloca [1 x i8**]
44+
// CHECK: [[FIRST_REQ:%.*]] = getelementptr inbounds [1 x i8**], [1 x i8**]* [[CONDITIONAL_REQ_BUFFER]]
45+
// CHECK: call i8** @_T033conditional_conformance_recursive7WrapperVyxGAA2P2A2aERzrlWa(%swift.type* [[WRAPPER_TO_A:%.*]], i8*** [[FIRST_REQ]], i64 1)

0 commit comments

Comments
 (0)