Skip to content

Commit 25d58f9

Browse files
authored
Merge pull request #5338 from slavapestov/fix-witness-thunk-archetypes
Fix witness thunk archetypes
2 parents 97f1963 + 58f013e commit 25d58f9

File tree

6 files changed

+108
-96
lines changed

6 files changed

+108
-96
lines changed

lib/AST/Substitution.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Substitution Substitution::subst(Module *module,
8585
proto->isSpecificProtocol(KnownProtocolKind::AnyObject)) {
8686
auto classDecl
8787
= substReplacement->getClassOrBoundGenericClass();
88+
assert(classDecl);
8889
SmallVector<ProtocolConformance *, 1> lookupResults;
8990
classDecl->lookupConformance(classDecl->getParentModule(),
9091
proto, lookupResults);

lib/SILGen/SILGenDecl.cpp

Lines changed: 90 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,9 @@ SILGenModule::getWitnessTable(ProtocolConformance *conformance) {
16621662
static bool maybeOpenCodeProtocolWitness(SILGenFunction &gen,
16631663
ProtocolConformance *conformance,
16641664
SILLinkage linkage,
1665+
Type selfInterfaceType,
1666+
Type selfType,
1667+
GenericEnvironment *genericEnv,
16651668
SILDeclRef requirement,
16661669
SILDeclRef witness,
16671670
ArrayRef<Substitution> witnessSubs) {
@@ -1670,7 +1673,8 @@ static bool maybeOpenCodeProtocolWitness(SILGenFunction &gen,
16701673
auto reqFn = cast<FuncDecl>(requirement.getDecl());
16711674
assert(reqFn->getAccessorKind() == AccessorKind::IsMaterializeForSet);
16721675
return gen.maybeEmitMaterializeForSetThunk(conformance, linkage,
1673-
reqFn, witnessFn,
1676+
selfInterfaceType, selfType,
1677+
genericEnv, reqFn, witnessFn,
16741678
witnessSubs);
16751679
}
16761680
}
@@ -1689,13 +1693,11 @@ static bool isSelfDerived(Type selfTy, Type t) {
16891693
/// requirement's type to get the type of the witness.
16901694
static CanAnyFunctionType
16911695
substSelfTypeIntoProtocolRequirementType(SILModule &M,
1696+
GenericEnvironment *reqtEnv,
16921697
CanGenericFunctionType reqtTy,
1693-
ProtocolConformance *conformance) {
1694-
if (conformance == nullptr) {
1695-
// Default witness thunks just get the requirement type without
1696-
// substituting Self.
1697-
return reqtTy;
1698-
}
1698+
ProtocolConformance *conformance,
1699+
GenericEnvironment *&genericEnv,
1700+
SubstitutionMap &thunkSubs) {
16991701

17001702
auto &C = M.getASTContext();
17011703

@@ -1732,26 +1734,27 @@ substSelfTypeIntoProtocolRequirementType(SILModule &M,
17321734

17331735
// Now we look at the generic signature of the requirement.
17341736
// We are going to drop `Self`, and requirements rooted in `Self`.
1735-
for (auto *param : reqtTy->getGenericParams().slice(1)) {
1737+
for (auto *param : reqtEnv->getGenericParams().slice(1)) {
17361738
allParams.push_back(param);
17371739
builder.addGenericParameter(param);
17381740
}
17391741

17401742
RequirementSource source(RequirementSource::Explicit, SourceLoc());
17411743

17421744
for (auto &reqt : reqtTy->getRequirements()) {
1743-
if (isSelfDerived(selfTy, reqt.getFirstType()))
1744-
continue;
1745-
17461745
switch (reqt.getKind()) {
17471746
case RequirementKind::Conformance:
17481747
case RequirementKind::Superclass:
17491748
case RequirementKind::WitnessMarker:
1749+
if (isSelfDerived(selfTy, reqt.getFirstType()))
1750+
continue;
1751+
17501752
builder.addRequirement(reqt, source);
17511753
break;
17521754

17531755
case RequirementKind::SameType: {
1754-
if (isSelfDerived(selfTy, reqt.getSecondType()))
1756+
if (isSelfDerived(selfTy, reqt.getFirstType()) &&
1757+
isSelfDerived(selfTy, reqt.getSecondType()))
17551758
continue;
17561759

17571760
// Substitute the constrained types.
@@ -1774,67 +1777,60 @@ substSelfTypeIntoProtocolRequirementType(SILModule &M,
17741777
auto input = reqtTy->getInput().subst(subs)->getCanonicalType();
17751778
auto result = reqtTy->getResult().subst(subs)->getCanonicalType();
17761779

1780+
CanAnyFunctionType reqtSubstTy;
1781+
1782+
auto addArchetypeSubstitution = [&](ArchetypeType *archetypeTy, Type substTy) {
1783+
auto contextTy = ArchetypeBuilder::mapTypeIntoContext(
1784+
M.getSwiftModule(), genericEnv, substTy);
1785+
1786+
thunkSubs.addSubstitution(CanType(archetypeTy), contextTy);
1787+
1788+
SmallVector<ProtocolConformanceRef, 2> conformances;
1789+
for (auto proto : archetypeTy->getConformsTo())
1790+
conformances.push_back(ProtocolConformanceRef(proto));
1791+
thunkSubs.addConformances(CanType(archetypeTy),
1792+
C.AllocateCopy(conformances));
1793+
};
1794+
17771795
// The result might be fully concrete, if the witness had no generic
17781796
// signature, and the requirement had no additional generic parameters
17791797
// beyond `Self`.
17801798
if (!allParams.empty()) {
17811799
builder.finalize(SourceLoc());
17821800

17831801
auto *sig = builder.getGenericSignature();
1802+
genericEnv = builder.getGenericEnvironment();
1803+
1804+
// Outer generic parameters come from the generic context of
1805+
// the conformance (which might not be the same as the generic
1806+
// context of the witness, if the witness is defined in a
1807+
// superclass, concrete extension or protocol extension).
1808+
if (auto *outerEnv = conformance->getGenericEnvironment()) {
1809+
for (auto pair : outerEnv->getArchetypeToInterfaceMap()) {
1810+
auto archetypeTy = pair.first->castTo<ArchetypeType>();
1811+
auto substTy = pair.second;
1812+
addArchetypeSubstitution(archetypeTy, substTy);
1813+
}
1814+
}
17841815

1785-
return cast<GenericFunctionType>(
1816+
reqtSubstTy = cast<GenericFunctionType>(
17861817
GenericFunctionType::get(sig, input, result, reqtTy->getExtInfo())
17871818
->getCanonicalType());
1788-
}
1789-
1790-
return CanFunctionType::get(input, result, reqtTy->getExtInfo());
1791-
}
1792-
1793-
static GenericEnvironment *
1794-
getSubstitutedGenericEnvironment(SILModule &M,
1795-
GenericEnvironment *reqtEnv,
1796-
CanGenericSignature witnessSig,
1797-
ProtocolConformance *conformance) {
1798-
if (conformance == nullptr) {
1799-
// Default witness thunks just use the context archetypes of the requirement.
1800-
return reqtEnv;
1801-
}
1802-
1803-
SmallVector<GenericTypeParamType *, 4> genericParamTypes;
1804-
TypeSubstitutionMap witnessContextParams;
1805-
1806-
auto selfTy = conformance->getProtocol()->getSelfInterfaceType()
1807-
->getCanonicalType();
1819+
} else {
1820+
genericEnv = nullptr;
18081821

1809-
// Outer generic parameters come from the generic context of
1810-
// the conformance (which might not be the same as the generic
1811-
// context of the witness, if the witness is defined in a
1812-
// superclass, concrete extension or protocol extension).
1813-
if (auto *outerEnv = conformance->getGenericEnvironment()) {
1814-
witnessContextParams = outerEnv->getInterfaceToArchetypeMap();
1815-
for (auto *paramTy : outerEnv->getGenericParams())
1816-
genericParamTypes.push_back(paramTy);
1822+
reqtSubstTy = CanFunctionType::get(input, result, reqtTy->getExtInfo());
18171823
}
18181824

1819-
for (auto *paramTy : reqtEnv->getGenericParams().slice(1))
1820-
genericParamTypes.push_back(paramTy);
1821-
18221825
// Inner generic parameters come from the requirement and
18231826
// also map to the archetypes of the requirement.
1824-
for (auto pair : reqtEnv->getInterfaceToArchetypeMap()) {
1825-
// Skip the 'Self' parameter and friends.
1826-
if (isSelfDerived(selfTy, pair.first))
1827-
continue;
1828-
1829-
auto result = witnessContextParams.insert(pair);
1830-
assert(result.second);
1827+
for (auto pair : reqtEnv->getArchetypeToInterfaceMap()) {
1828+
auto archetypeTy = pair.first->castTo<ArchetypeType>();
1829+
auto substTy = pair.second.subst(subs);
1830+
addArchetypeSubstitution(archetypeTy, substTy);
18311831
}
18321832

1833-
if (!witnessContextParams.empty())
1834-
return GenericEnvironment::get(M.getASTContext(), genericParamTypes,
1835-
witnessContextParams);
1836-
1837-
return nullptr;
1833+
return reqtSubstTy;
18381834
}
18391835

18401836
SILFunction *
@@ -1845,7 +1841,6 @@ SILGenModule::emitProtocolWitness(ProtocolConformance *conformance,
18451841
IsFreeFunctionWitness_t isFree,
18461842
ArrayRef<Substitution> witnessSubs) {
18471843
auto requirementInfo = Types.getConstantInfo(requirement);
1848-
auto witnessInfo = Types.getConstantInfo(witness);
18491844
unsigned witnessUncurryLevel = witness.uncurryLevel;
18501845

18511846
// If the witness is a free function, consider the self argument
@@ -1859,11 +1854,37 @@ SILGenModule::emitProtocolWitness(ProtocolConformance *conformance,
18591854
assert(requirement.uncurryLevel == witnessUncurryLevel &&
18601855
"uncurry level of requirement and witness do not match");
18611856

1857+
GenericEnvironment *genericEnv = nullptr;
1858+
18621859
// Work out the lowered function type of the SIL witness thunk.
18631860
auto reqtOrigTy
18641861
= cast<GenericFunctionType>(requirementInfo.LoweredInterfaceType);
1865-
auto reqtSubstTy
1866-
= substSelfTypeIntoProtocolRequirementType(M, reqtOrigTy, conformance);
1862+
CanAnyFunctionType reqtSubstTy;
1863+
if (conformance == nullptr) {
1864+
reqtSubstTy = reqtOrigTy;
1865+
genericEnv = requirementInfo.GenericEnv;
1866+
} else {
1867+
SubstitutionMap thunkSubs;
1868+
reqtSubstTy
1869+
= substSelfTypeIntoProtocolRequirementType(M, requirementInfo.GenericEnv,
1870+
reqtOrigTy, conformance,
1871+
genericEnv, thunkSubs);
1872+
1873+
// Remap witness substitutions to use the correct archetypes.
1874+
// Sema produces witness substitutions using a mix of archetypes
1875+
// from the requirement and the witness. To solve downstream
1876+
// issues in the SIL optimizer, we build all-new archetypes
1877+
// using the correct generic signature here.
1878+
//
1879+
// FIXME: Once Sema records witness substitutions in terms of
1880+
// interface types, this code as well as the code that builds
1881+
// thunkSubs in substSelfTypeIntoProtocolRequirementType() can
1882+
// be removed.
1883+
auto newWitnessSubs = getASTContext().AllocateCopy(witnessSubs);
1884+
for (unsigned i = 0, e = witnessSubs.size(); i < e; i++)
1885+
newWitnessSubs[i] = witnessSubs[i].subst(M.getSwiftModule(), thunkSubs);
1886+
witnessSubs = newWitnessSubs;
1887+
}
18671888

18681889
// Lower the witness thunk type with the requirement's abstraction level.
18691890
auto witnessSILFnType = getNativeSILFunctionType(M,
@@ -1899,16 +1920,6 @@ SILGenModule::emitProtocolWitness(ProtocolConformance *conformance,
18991920
nameBuffer = mangler.finalize();
19001921
}
19011922

1902-
CanGenericSignature witnessSig;
1903-
if (auto gft = dyn_cast<GenericFunctionType>(
1904-
witnessInfo.LoweredInterfaceType))
1905-
witnessSig = gft.getGenericSignature();
1906-
1907-
// Collect the generic environment for the witness.
1908-
GenericEnvironment *requirementEnv = requirementInfo.GenericEnv;
1909-
GenericEnvironment *witnessEnv = getSubstitutedGenericEnvironment(
1910-
M, requirementEnv, witnessSig, conformance);
1911-
19121923
// If the thunked-to function is set to be always inlined, do the
19131924
// same with the witness, on the theory that the user wants all
19141925
// calls removed if possible, e.g. when we're able to devirtualize
@@ -1927,7 +1938,7 @@ SILGenModule::emitProtocolWitness(ProtocolConformance *conformance,
19271938

19281939
auto *f = M.createFunction(
19291940
linkage, nameBuffer, witnessSILFnType,
1930-
witnessEnv, SILLocation(witness.getDecl()),
1941+
genericEnv, SILLocation(witness.getDecl()),
19311942
IsNotBare, IsTransparent, isFragile, IsThunk,
19321943
SILFunction::NotRelevant, InlineStrategy);
19331944

@@ -1937,27 +1948,28 @@ SILGenModule::emitProtocolWitness(ProtocolConformance *conformance,
19371948
PrettyStackTraceSILFunction trace("generating protocol witness thunk", f);
19381949

19391950
// Create the witness.
1951+
Type selfInterfaceType;
19401952
Type selfType;
19411953

19421954
// If the witness is a free function, there is no Self type.
19431955
if (!isFree) {
1944-
// If we are emitting a witness thunk for a concrete conformance, Self is
1945-
// just the conforming type.
19461956
if (conformance) {
1947-
selfType = conformance->getType();
1948-
1949-
// For default implementations, Self is the protocol archetype.
1957+
selfInterfaceType = conformance->getInterfaceType();
19501958
} else {
19511959
auto *proto = cast<ProtocolDecl>(requirement.getDecl()->getDeclContext());
1952-
selfType = proto->getSelfTypeInContext();
1960+
selfInterfaceType = proto->getSelfInterfaceType();
19531961
}
1962+
1963+
selfType = ArchetypeBuilder::mapTypeIntoContext(
1964+
M.getSwiftModule(), genericEnv, selfInterfaceType);
19541965
}
19551966

19561967
SILGenFunction gen(*this, *f);
19571968

19581969
// Open-code certain protocol witness "thunks".
1959-
if (maybeOpenCodeProtocolWitness(gen, conformance, linkage, requirement,
1960-
witness, witnessSubs)) {
1970+
if (maybeOpenCodeProtocolWitness(gen, conformance, linkage,
1971+
selfInterfaceType, selfType, genericEnv,
1972+
requirement, witness, witnessSubs)) {
19611973
assert(!isFree);
19621974
return f;
19631975
}

lib/SILGen/SILGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
11221122
SILValue buffer, SILValue callbackStorage);
11231123
bool maybeEmitMaterializeForSetThunk(ProtocolConformance *conformance,
11241124
SILLinkage linkage,
1125+
Type selfInterfaceType,
1126+
Type selfType,
1127+
GenericEnvironment *genericEnv,
11251128
FuncDecl *requirement,
11261129
FuncDecl *witness,
11271130
ArrayRef<Substitution> witnessSubs);

lib/SILGen/SILGenMaterializeForSet.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,31 +160,22 @@ struct MaterializeForSetEmitter {
160160

161161
static MaterializeForSetEmitter
162162
forWitnessThunk(SILGenModule &SGM,
163-
ProtocolConformance *conformance,
164-
SILLinkage linkage,
163+
ProtocolConformance *conformance, SILLinkage linkage,
164+
Type selfInterfaceType, Type selfType,
165+
GenericEnvironment *genericEnv,
165166
FuncDecl *requirement, FuncDecl *witness,
166167
ArrayRef<Substitution> witnessSubs) {
167-
Type selfInterfaceType, selfType;
168-
if (conformance) {
169-
selfInterfaceType = conformance->getInterfaceType();
170-
selfType = conformance->getType();
171-
} else {
172-
auto *proto = cast<ProtocolDecl>(requirement->getDeclContext());
173-
selfInterfaceType = proto->getSelfInterfaceType();
174-
selfType = proto->getSelfTypeInContext();
175-
}
176-
177168
MaterializeForSetEmitter emitter(SGM, linkage, witness, witnessSubs,
178169
selfInterfaceType, selfType);
179170

180171
if (conformance) {
181172
if (auto signature = conformance->getGenericSignature())
182173
emitter.GenericSig = signature->getCanonicalSignature();
183-
emitter.GenericEnv = conformance->getGenericEnvironment();
174+
emitter.GenericEnv = genericEnv;
184175
} else {
185176
auto signature = requirement->getGenericSignatureOfContext();
186177
emitter.GenericSig = signature->getCanonicalSignature();
187-
emitter.GenericEnv = requirement->getGenericEnvironmentOfContext();
178+
emitter.GenericEnv = genericEnv;
188179
}
189180

190181
emitter.RequirementStorage = requirement->getAccessorStorageDecl();
@@ -816,14 +807,17 @@ MaterializeForSetEmitter::createSetterCallback(SILFunction &F,
816807
bool SILGenFunction::
817808
maybeEmitMaterializeForSetThunk(ProtocolConformance *conformance,
818809
SILLinkage linkage,
810+
Type selfInterfaceType,
811+
Type selfType,
812+
GenericEnvironment *genericEnv,
819813
FuncDecl *requirement,
820814
FuncDecl *witness,
821815
ArrayRef<Substitution> witnessSubs) {
822816

823817
MaterializeForSetEmitter emitter
824818
= MaterializeForSetEmitter::forWitnessThunk(
825-
SGM, conformance, linkage, requirement, witness,
826-
witnessSubs);
819+
SGM, conformance, linkage, selfInterfaceType, selfType,
820+
genericEnv, requirement, witness, witnessSubs);
827821

828822
if (!emitter.shouldOpenCode())
829823
return false;

test/SILGen/generic_witness.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -emit-ir %s
23

34
protocol Runcible {
45
func runce<A>(_ x: A)
@@ -49,7 +50,7 @@ struct Canvas<I : Ink> where I.Paint : Pen {
4950

5051
extension Canvas : Medium {}
5152

52-
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWuRx15generic_witness3Inkwx5PaintS_3PenrGVS_6Canvasx_S_6MediumS_FS4_4drawuRd__S_6Pencilwd__6StrokezWx7TextureS1__rfT5paintWxS7_S1__6pencilqd___T_ : $@convention(witness_method) <I where I : Ink, I.Paint : Pen><P where P : Pencil> (@in I.Paint, @in P, @in_guaranteed Canvas<I>) -> () {
53+
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWuRx15generic_witness3Inkwx5PaintS_3PenrGVS_6Canvasx_S_6MediumS_FS4_4drawuRd__S_6Pencilwd__6StrokezWx7TextureS1__rfT5paintWxS7_S1__6pencilqd___T_ : $@convention(witness_method) <I where I : Ink, I.Paint : Pen><P where P : Pencil, I.Paint == P.Stroke> (@in I.Paint, @in P, @in_guaranteed Canvas<I>) -> ()
5354
// CHECK: [[FN:%.*]] = function_ref @_TFV15generic_witness6Canvas4drawuRd__S_6Pencilwx5Paintzwd__6StrokerfT5paintwxS2_6pencilqd___T_ : $@convention(method) <τ_0_0 where τ_0_0 : Ink, τ_0_0.Paint : Pen><τ_1_0 where τ_1_0 : Pencil, τ_0_0.Paint == τ_1_0.Stroke> (@in τ_0_0.Paint, @in τ_1_0, Canvas<τ_0_0>) -> ()
5455
// CHECK: apply [[FN]]<I, P, I.Paint>({{.*}}) : $@convention(method) <τ_0_0 where τ_0_0 : Ink, τ_0_0.Paint : Pen><τ_1_0 where τ_1_0 : Pencil, τ_0_0.Paint == τ_1_0.Stroke> (@in τ_0_0.Paint, @in τ_1_0, Canvas<τ_0_0>) -> ()
5556
// CHECK: }

test/SILGen/witness_same_type.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -emit-ir %s
23

34
protocol Fooable {
45
associatedtype Bar
@@ -10,7 +11,7 @@ struct X {}
1011

1112
// Ensure that the protocol witness for requirements with same-type constraints
1213
// is set correctly. <rdar://problem/16369105>
13-
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV17witness_same_type3FooS_7FooableS_FS1_3foo{{.*}} : $@convention(witness_method) <T where T : Fooable> (@in T, @in_guaranteed Foo) -> @out X
14+
// CHECK-LABEL: sil hidden [transparent] [thunk] @_TTWV17witness_same_type3FooS_7FooableS_FS1_3foouRd__S1_wx3Barzwd__S2_rfT1xqd___wxS2_ : $@convention(witness_method) <T where T : Fooable, T.Bar == X> (@in T, @in_guaranteed Foo) -> @out X
1415
struct Foo: Fooable {
1516
typealias Bar = X
1617

0 commit comments

Comments
 (0)