Skip to content

Commit 8052a61

Browse files
authored
Merge pull request #28430 from atrick/fix-existential-conformance
Fix ExistentialSpecializer: inherited conformance
2 parents be1d896 + 7d0a772 commit 8052a61

File tree

5 files changed

+123
-20
lines changed

5 files changed

+123
-20
lines changed

include/swift/AST/ASTContext.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -875,9 +875,9 @@ class ASTContext final {
875875
CanGenericSignature getSingleGenericParameterSignature() const;
876876

877877
/// Retrieve a generic signature with a single type parameter conforming
878-
/// to the given existential type.
879-
CanGenericSignature getExistentialSignature(CanType existential,
880-
ModuleDecl *mod);
878+
/// to the given opened archetype.
879+
CanGenericSignature getOpenedArchetypeSignature(CanType existential,
880+
ModuleDecl *mod);
881881

882882
GenericSignature getOverrideGenericSignature(const ValueDecl *base,
883883
const ValueDecl *derived);

lib/AST/ASTContext.cpp

+11-4
Original file line numberDiff line numberDiff line change
@@ -3693,8 +3693,8 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const {
36933693
auto thisType = Type(const_cast<OpenedArchetypeType*>(this));
36943694
auto &ctx = thisType->getASTContext();
36953695
// Create a generic environment to represent the opened type.
3696-
auto signature = ctx.getExistentialSignature(Opened->getCanonicalType(),
3697-
nullptr);
3696+
auto signature =
3697+
ctx.getOpenedArchetypeSignature(Opened->getCanonicalType(), nullptr);
36983698
auto *builder = signature->getGenericSignatureBuilder();
36993699
auto *env = GenericEnvironment::getIncomplete(signature, builder);
37003700
env->addMapping(signature->getGenericParams()[0], thisType);
@@ -4327,8 +4327,15 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const {
43274327
return canonicalSig;
43284328
}
43294329

4330-
CanGenericSignature ASTContext::getExistentialSignature(CanType existential,
4331-
ModuleDecl *mod) {
4330+
// Return the signature for an opened existential. The opened archetype may have
4331+
// a different set of conformances from the corresponding existential. The
4332+
// opened archetype conformances are dictated by the ABI for generic arguments,
4333+
// while the existential value conformances are dictated by their layout (see
4334+
// Type::getExistentialLayout()). In particular, the opened archetype signature
4335+
// does not have requirements for conformances inherited from superclass
4336+
// constraints while existential values do.
4337+
CanGenericSignature ASTContext::getOpenedArchetypeSignature(CanType existential,
4338+
ModuleDecl *mod) {
43324339
auto found = getImpl().ExistentialSignatures.find(existential);
43334340
if (found != getImpl().ExistentialSignatures.end())
43344341
return found->second;

lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp

+24-9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#define DEBUG_TYPE "sil-existential-transform"
1818
#include "ExistentialTransform.h"
19+
#include "swift/AST/ExistentialLayout.h"
1920
#include "swift/AST/GenericEnvironment.h"
2021
#include "swift/AST/TypeCheckRequests.h"
2122
#include "swift/SIL/OptimizationRemark.h"
@@ -114,11 +115,29 @@ void ExistentialSpecializerCloner::cloneAndPopulateFunction() {
114115
}
115116
}
116117

118+
// Gather the conformances needed for an existential value based on an opened
119+
// archetype. This adds any conformances inherited from superclass constraints.
120+
static ArrayRef<ProtocolConformanceRef>
121+
collectExistentialConformances(ModuleDecl *M, CanType openedType,
122+
CanType existentialType) {
123+
assert(!openedType.isAnyExistentialType());
124+
125+
auto layout = existentialType.getExistentialLayout();
126+
auto protocols = layout.getProtocols();
127+
128+
SmallVector<ProtocolConformanceRef, 4> conformances;
129+
for (auto proto : protocols) {
130+
auto conformance = M->lookupConformance(openedType, proto->getDecl());
131+
assert(conformance);
132+
conformances.push_back(conformance);
133+
}
134+
return M->getASTContext().AllocateCopy(conformances);
135+
}
136+
117137
// Create the entry basic block with the function arguments.
118138
void ExistentialSpecializerCloner::cloneArguments(
119139
SmallVectorImpl<SILValue> &entryArgs) {
120140
auto &M = OrigF->getModule();
121-
auto &Ctx = M.getASTContext();
122141

123142
// Create the new entry block.
124143
SILFunction &NewF = getBuilder().getFunction();
@@ -164,14 +183,10 @@ void ExistentialSpecializerCloner::cloneArguments(
164183
NewArg->setOwnershipKind(ValueOwnershipKind(
165184
NewF, GenericSILType, ArgDesc.Arg->getArgumentConvention()));
166185
// Determine the Conformances.
167-
SmallVector<ProtocolConformanceRef, 1> NewConformances;
168-
auto ContextTy = NewF.mapTypeIntoContext(GenericParam);
169-
auto OpenedArchetype = ContextTy->castTo<ArchetypeType>();
170-
for (auto proto : OpenedArchetype->getConformsTo()) {
171-
NewConformances.push_back(ProtocolConformanceRef(proto));
172-
}
173-
ArrayRef<ProtocolConformanceRef> Conformances =
174-
Ctx.AllocateCopy(NewConformances);
186+
SILType ExistentialType = ArgDesc.Arg->getType().getObjectType();
187+
CanType OpenedType = NewArg->getType().getASTType();
188+
auto Conformances = collectExistentialConformances(
189+
M.getSwiftModule(), OpenedType, ExistentialType.getASTType());
175190
auto ExistentialRepr =
176191
ArgDesc.Arg->getType().getPreferredExistentialRepresentation();
177192
auto &EAD = ExistentialArgDescriptor[ArgDesc.Index];

lib/SILOptimizer/Utils/Existential.cpp

+27-4
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,34 @@ void ConcreteExistentialInfo::initializeSubstitutionMap(
243243

244244
// Construct a single-generic-parameter substitution map directly to the
245245
// ConcreteType with this existential's full list of conformances.
246+
//
247+
// NOTE: getOpenedArchetypeSignature() generates the signature for passing an
248+
// opened existential as a generic parameter. No opened archetypes are
249+
// actually involved here--the API is only used as a convenient way to create
250+
// a substitution map. Since opened archetypes have different conformances
251+
// than their corresponding existential, ExistentialConformances needs to be
252+
// filtered when using it with this (phony) generic signature.
246253
CanGenericSignature ExistentialSig =
247-
M->getASTContext().getExistentialSignature(ExistentialType,
248-
M->getSwiftModule());
249-
ExistentialSubs = SubstitutionMap::get(ExistentialSig, {ConcreteType},
250-
ExistentialConformances);
254+
M->getASTContext().getOpenedArchetypeSignature(ExistentialType,
255+
M->getSwiftModule());
256+
ExistentialSubs = SubstitutionMap::get(
257+
ExistentialSig, [&](SubstitutableType *type) { return ConcreteType; },
258+
[&](CanType /*depType*/, Type /*replaceType*/,
259+
ProtocolDecl *proto) -> ProtocolConformanceRef {
260+
// Directly providing ExistentialConformances to the SubstitionMap will
261+
// fail because of the mismatch between opened archetype conformance and
262+
// existential value conformance. Instead, provide a conformance lookup
263+
// function that pulls only the necessary conformances out of
264+
// ExistentialConformances. This assumes that existential conformances
265+
// are a superset of opened archetype conformances.
266+
auto iter =
267+
llvm::find_if(ExistentialConformances,
268+
[&](const ProtocolConformanceRef &conformance) {
269+
return conformance.getRequirement() == proto;
270+
});
271+
assert(iter != ExistentialConformances.end() && "missing conformance");
272+
return *iter;
273+
});
251274
assert(isValid());
252275
}
253276

test/SILOptimizer/existential_transform_extras.sil

+58
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,61 @@ sil_witness_table hidden Klass1: P module dealloc {
203203
sil_witness_table hidden Klass2: P module dealloc {
204204
method #P.foo!1: <Self where Self : P> (Self) -> () -> Int32 : nil
205205
}
206+
207+
// -----------------------------------------------------------------------------
208+
// Test composite conformances with superclass constraints where one of
209+
// the protocol constraints is satisfied by the superclass constraint.
210+
//
211+
// <rdar://problem/57025861> "Assertion failed: (conformances.size()
212+
// == numConformanceRequirements)" in ExistentialSpecializer on
213+
protocol Plotable {}
214+
215+
class PlotLayer : Plotable {
216+
init()
217+
}
218+
219+
protocol InView {}
220+
221+
class PlotLayerInView : PlotLayer & InView {
222+
override init()
223+
}
224+
225+
class PlotView {
226+
@_hasStorage @_hasInitialValue var layers: Container<PlotLayer & Plotable & InView> { get set }
227+
public func resolveLayers()
228+
init()
229+
}
230+
231+
struct Container<T> {
232+
@_hasStorage @_hasInitialValue var val: T { get set }
233+
}
234+
235+
// Check that the init_existential instruction was created with all
236+
// three requirements (the generic parameter only has two
237+
// requirements). Relies on assertions during specialization and on
238+
// the SILVerifier to catch other inconsistencies.
239+
//
240+
// CHECK-LABEL: sil shared @$s40testExistentialSpecializeCompositeHelperTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : PlotLayer, τ_0_0 : InView> (@owned τ_0_0, @inout Container<PlotLayer & InView & Plotable>) -> () {
241+
// CHECK: bb0(%0 : $τ_0_0, %1 : $*Container<PlotLayer & InView & Plotable>):
242+
// CHECK: init_existential_ref %0 : $τ_0_0 : $τ_0_0, $PlotLayer & InView & Plotable
243+
// CHECK-LABEL: } // end sil function '$s40testExistentialSpecializeCompositeHelperTf4en_n'
244+
sil shared @testExistentialSpecializeCompositeHelper : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container<PlotLayer & Plotable & InView>) -> () {
245+
bb0(%0 : $PlotLayer & Plotable & InView, %1 : $*Container<PlotLayer & Plotable & InView>):
246+
%adr = struct_element_addr %1 : $*Container<PlotLayer & Plotable & InView>, #Container.val
247+
store %0 to %adr : $*PlotLayer & Plotable & InView
248+
%v = tuple ()
249+
return %v : $()
250+
}
251+
252+
sil @testExistentialSpecializeComposite : $@convention(method) (@guaranteed PlotView) -> () {
253+
bb0(%0 : $PlotView):
254+
%ref = alloc_ref $PlotLayerInView
255+
strong_retain %ref : $PlotLayerInView
256+
%exis = init_existential_ref %ref : $PlotLayerInView : $PlotLayerInView, $PlotLayer & Plotable & InView
257+
%array = ref_element_addr %0 : $PlotView, #PlotView.layers
258+
%f = function_ref @testExistentialSpecializeCompositeHelper : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container<PlotLayer & Plotable & InView>) -> ()
259+
%call = apply %f(%exis, %array) : $@convention(method) (@owned PlotLayer & Plotable & InView, @inout Container<PlotLayer & Plotable & InView>) -> ()
260+
strong_release %ref : $PlotLayerInView
261+
%v = tuple ()
262+
return %v : $()
263+
}

0 commit comments

Comments
 (0)