Skip to content

Commit 7d0a772

Browse files
committed
Fix ExistentialSpecializer: inherited conformance
The ExistentialSpecializer incorrectly assumed that an existential's conformances match an opened archetype. They don't. Opened archetypes strip inherited conformances per the ABI for generic argument passing. Existential values retain those inherited conformances (for some inexplicable reason). - Rename ASTContext::getExistentialSignature() to getOpenedArchetypeSiganture() because it was doing exactly the wrong thing for existentials. - Fix ConcreteExistentialInfo to produce the correct SubstitutionMap. - Fix ExistentialSpecializer to generate the correct conformances for init_existential by adding a collectExistentialConformances() helper. Fixes <rdar://problem/57025861> "Assertion failed: (conformances.size() == numConformanceRequirements)" in ExistentialSpecializer on inlined code
1 parent 191f9db commit 7d0a772

File tree

5 files changed

+123
-20
lines changed

5 files changed

+123
-20
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -866,9 +866,9 @@ class ASTContext final {
866866
CanGenericSignature getSingleGenericParameterSignature() const;
867867

868868
/// Retrieve a generic signature with a single type parameter conforming
869-
/// to the given existential type.
870-
CanGenericSignature getExistentialSignature(CanType existential,
871-
ModuleDecl *mod);
869+
/// to the given opened archetype.
870+
CanGenericSignature getOpenedArchetypeSignature(CanType existential,
871+
ModuleDecl *mod);
872872

873873
GenericSignature getOverrideGenericSignature(const ValueDecl *base,
874874
const ValueDecl *derived);

lib/AST/ASTContext.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3689,8 +3689,8 @@ GenericEnvironment *OpenedArchetypeType::getGenericEnvironment() const {
36893689
auto thisType = Type(const_cast<OpenedArchetypeType*>(this));
36903690
auto &ctx = thisType->getASTContext();
36913691
// Create a generic environment to represent the opened type.
3692-
auto signature = ctx.getExistentialSignature(Opened->getCanonicalType(),
3693-
nullptr);
3692+
auto signature =
3693+
ctx.getOpenedArchetypeSignature(Opened->getCanonicalType(), nullptr);
36943694
auto *builder = signature->getGenericSignatureBuilder();
36953695
auto *env = GenericEnvironment::getIncomplete(signature, builder);
36963696
env->addMapping(signature->getGenericParams()[0], thisType);
@@ -4323,8 +4323,15 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const {
43234323
return canonicalSig;
43244324
}
43254325

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

lib/SILOptimizer/FunctionSignatureTransforms/ExistentialTransform.cpp

Lines changed: 24 additions & 9 deletions
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

Lines changed: 27 additions & 4 deletions
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

Lines changed: 58 additions & 0 deletions
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)