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
This commit is contained in:
Andrew Trick
2019-11-21 20:39:36 -08:00
parent 191f9db59f
commit 7d0a772542
5 changed files with 123 additions and 20 deletions

View File

@@ -243,11 +243,34 @@ void ConcreteExistentialInfo::initializeSubstitutionMap(
// Construct a single-generic-parameter substitution map directly to the
// ConcreteType with this existential's full list of conformances.
//
// NOTE: getOpenedArchetypeSignature() generates the signature for passing an
// opened existential as a generic parameter. No opened archetypes are
// actually involved here--the API is only used as a convenient way to create
// a substitution map. Since opened archetypes have different conformances
// than their corresponding existential, ExistentialConformances needs to be
// filtered when using it with this (phony) generic signature.
CanGenericSignature ExistentialSig =
M->getASTContext().getExistentialSignature(ExistentialType,
M->getSwiftModule());
ExistentialSubs = SubstitutionMap::get(ExistentialSig, {ConcreteType},
ExistentialConformances);
M->getASTContext().getOpenedArchetypeSignature(ExistentialType,
M->getSwiftModule());
ExistentialSubs = SubstitutionMap::get(
ExistentialSig, [&](SubstitutableType *type) { return ConcreteType; },
[&](CanType /*depType*/, Type /*replaceType*/,
ProtocolDecl *proto) -> ProtocolConformanceRef {
// Directly providing ExistentialConformances to the SubstitionMap will
// fail because of the mismatch between opened archetype conformance and
// existential value conformance. Instead, provide a conformance lookup
// function that pulls only the necessary conformances out of
// ExistentialConformances. This assumes that existential conformances
// are a superset of opened archetype conformances.
auto iter =
llvm::find_if(ExistentialConformances,
[&](const ProtocolConformanceRef &conformance) {
return conformance.getRequirement() == proto;
});
assert(iter != ExistentialConformances.end() && "missing conformance");
return *iter;
});
assert(isValid());
}