Canonicalize conformance access paths for sources pre-requirement-signature.

When a requirement source involving a ProtocolRequirement element is
built prior to the requirement signature of the protocol it
references, we can end up with a requirement source whose steps don't
reflect was is actually available via the requirement signatures. When
building a conformance access path from such requirement sources,
canonicalize on-the-fly using the requirement signatures (which have
been/can be computed by this point) to produce a correct access path.
This commit is contained in:
Doug Gregor
2017-03-08 15:51:23 -08:00
parent 4cd76d9df4
commit 1f8b0f9b85
3 changed files with 106 additions and 30 deletions

View File

@@ -504,6 +504,7 @@ class GenericSignatureBuilder::RequirementSource final
WrittenRequirementLoc> { WrittenRequirementLoc> {
friend class FloatingRequirementSource; friend class FloatingRequirementSource;
friend class GenericSignature;
public: public:
enum Kind : uint8_t { enum Kind : uint8_t {
@@ -584,6 +585,9 @@ private:
/// Whether there is a trailing written requirement location. /// Whether there is a trailing written requirement location.
const bool hasTrailingWrittenRequirementLoc; const bool hasTrailingWrittenRequirementLoc;
/// Whether a protocol requirement came from the requirement signature.
const bool usesRequirementSignature;
/// The actual storage, described by \c storageKind. /// The actual storage, described by \c storageKind.
union { union {
/// The root archetype. /// The root archetype.
@@ -673,7 +677,7 @@ public:
WrittenRequirementLoc writtenReqLoc) WrittenRequirementLoc writtenReqLoc)
: kind(kind), storageKind(StorageKind::RootArchetype), : kind(kind), storageKind(StorageKind::RootArchetype),
hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()), hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()),
parent(nullptr) { usesRequirementSignature(false), parent(nullptr) {
assert(isAcceptableStorageKind(kind, storageKind) && assert(isAcceptableStorageKind(kind, storageKind) &&
"RequirementSource kind/storageKind mismatch"); "RequirementSource kind/storageKind mismatch");
@@ -689,7 +693,7 @@ public:
WrittenRequirementLoc writtenReqLoc) WrittenRequirementLoc writtenReqLoc)
: kind(kind), storageKind(StorageKind::StoredType), : kind(kind), storageKind(StorageKind::StoredType),
hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()), hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()),
usesRequirementSignature(protocol->isRequirementSignatureComputed()),
parent(parent) { parent(parent) {
assert((static_cast<bool>(parent) != isRootKind(kind)) && assert((static_cast<bool>(parent) != isRootKind(kind)) &&
"Root RequirementSource should not have parent (or vice versa)"); "Root RequirementSource should not have parent (or vice versa)");
@@ -707,7 +711,7 @@ public:
ProtocolConformance *conformance) ProtocolConformance *conformance)
: kind(kind), storageKind(StorageKind::ProtocolConformance), : kind(kind), storageKind(StorageKind::ProtocolConformance),
hasTrailingWrittenRequirementLoc(false), hasTrailingWrittenRequirementLoc(false),
parent(parent) { usesRequirementSignature(false), parent(parent) {
assert((static_cast<bool>(parent) != isRootKind(kind)) && assert((static_cast<bool>(parent) != isRootKind(kind)) &&
"Root RequirementSource should not have parent (or vice versa)"); "Root RequirementSource should not have parent (or vice versa)");
assert(isAcceptableStorageKind(kind, storageKind) && assert(isAcceptableStorageKind(kind, storageKind) &&
@@ -720,7 +724,7 @@ public:
AssociatedTypeDecl *assocType) AssociatedTypeDecl *assocType)
: kind(kind), storageKind(StorageKind::AssociatedTypeDecl), : kind(kind), storageKind(StorageKind::AssociatedTypeDecl),
hasTrailingWrittenRequirementLoc(false), hasTrailingWrittenRequirementLoc(false),
parent(parent) { usesRequirementSignature(false), parent(parent) {
assert((static_cast<bool>(parent) != isRootKind(kind)) && assert((static_cast<bool>(parent) != isRootKind(kind)) &&
"Root RequirementSource should not have parent (or vice versa)"); "Root RequirementSource should not have parent (or vice versa)");
assert(isAcceptableStorageKind(kind, storageKind) && assert(isAcceptableStorageKind(kind, storageKind) &&

View File

@@ -728,6 +728,16 @@ GenericEnvironment *CanGenericSignature::getGenericEnvironment(
module); module);
} }
/// Remove all of the associated type declarations from the given type
/// parameter, producing \c DependentMemberTypes with names alone.
static Type eraseAssociatedTypes(Type type) {
if (auto depMemTy = type->getAs<DependentMemberType>())
return DependentMemberType::get(eraseAssociatedTypes(depMemTy->getBase()),
depMemTy->getName());
return type;
}
ConformanceAccessPath GenericSignature::getConformanceAccessPath( ConformanceAccessPath GenericSignature::getConformanceAccessPath(
Type type, Type type,
ProtocolDecl *protocol, ProtocolDecl *protocol,
@@ -770,34 +780,74 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
#endif #endif
// Local function to construct the conformance access path from the // Local function to construct the conformance access path from the
// requirement // requirement.
std::function<void(const RequirementSource *, ProtocolDecl *)> buildPath; std::function<void(GenericSignature *, const RequirementSource *,
buildPath = [&](const RequirementSource *source, ProtocolDecl *)> buildPath;
buildPath = [&](GenericSignature *sig, const RequirementSource *source,
ProtocolDecl *conformingProto) { ProtocolDecl *conformingProto) {
// Each protocol requirement is a step along the path. // Each protocol requirement is a step along the path.
if (source->kind == RequirementSource::ProtocolRequirement) { if (source->kind == RequirementSource::ProtocolRequirement) {
// Follow the rest of the path to derive the conformance into which // Follow the rest of the path to derive the conformance into which
// this particular protocol requirement step would look. // this particular protocol requirement step would look.
auto inProtocol = source->getProtocolDecl(); auto inProtocol = source->getProtocolDecl();
buildPath(source->parent, inProtocol); buildPath(sig, source->parent, inProtocol);
assert(path.path.back().second == inProtocol &&
"path produces incorrect conformance");
// Add this step along the path, which involes looking for the // If this step was computed via the requirement signature, add it
// conformance we want (\c conformingProto) within the protocol // directly.
// described by this source. if (source->usesRequirementSignature) {
// Add this step along the path, which involes looking for the
// conformance we want (\c conformingProto) within the protocol
// described by this source.
// Canonicalize the subject type within the protocol's requirement // Canonicalize the subject type within the protocol's requirement
// signature. // signature.
Type subjectType = source->getStoredType(); // FIXME: We might be able to guarantee this when building from a
subjectType = inProtocol->getRequirementSignature() // requirement signature.
->getCanonicalTypeInContext(subjectType, Type subjectType = source->getStoredType();
*inProtocol->getParentModule()); subjectType = inProtocol->getRequirementSignature()
->getCanonicalTypeInContext(subjectType,
*inProtocol->getParentModule());
assert(hasConformanceInSignature(inProtocol->getRequirementSignature(), assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
subjectType, conformingProto) && subjectType, conformingProto) &&
"missing explicit conformance in requirement signature"); "missing explicit conformance in requirement signature");
// Record this step. // Record this step.
path.path.push_back({subjectType, conformingProto}); path.path.push_back({subjectType, conformingProto});
return;
}
// Canonicalize this step with respect to the requirement signature.
if (!inProtocol->isRequirementSignatureComputed()) {
inProtocol->computeRequirementSignature();
assert(inProtocol->isRequirementSignatureComputed() &&
"missing signature");
}
// Get a generic signature builder for the requirement signature. This has
// the requirement we need.
auto reqSig = inProtocol->getRequirementSignature();
auto &reqSigBuilder = *reqSig->getGenericSignatureBuilder(
*inProtocol->getModuleContext());
// Retrieve the stored type, but erase all of the specific associated
// type declarations; we don't want any details of the enclosing context
// to sneak in here.
Type storedType = eraseAssociatedTypes(source->getStoredType());
// Dig out the potential archetype for this stored type.
auto pa = reqSigBuilder.resolveArchetype(storedType);
auto rep = pa->getRepresentative();
// Find the conformance of this potential archetype to the protocol in
// question.
auto knownConforms = rep->getConformsTo().find(conformingProto);
assert(knownConforms != rep->getConformsTo().end());
// Build the path according to the requirement signature.
buildPath(reqSig, knownConforms->second, conformingProto);
// We're done. // We're done.
return; return;
@@ -805,7 +855,7 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
// If we still have a parent, keep going. // If we still have a parent, keep going.
if (source->parent) { if (source->parent) {
buildPath(source->parent, conformingProto); buildPath(sig, source->parent, conformingProto);
return; return;
} }
@@ -815,17 +865,24 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
// Retrieve the subject type, which is the archetype anchor for the root // Retrieve the subject type, which is the archetype anchor for the root
// of this requirement. // of this requirement.
auto anchor = auto subjectPA = source->getRootPotentialArchetype();
source->getRootPotentialArchetype()->getArchetypeAnchor(builder); subjectPA = subjectPA->getArchetypeAnchor(*subjectPA->getBuilder());
Type subjectType = anchor->getDependentType(getGenericParams(), false); Type subjectType = subjectPA->getDependentType(sig->getGenericParams(),
false);
assert(hasConformanceInSignature(this, subjectType, conformingProto) && // Skip trivial path elements. These occur when querying a requirement
// signature.
if (!path.path.empty() && conformingProto == path.path.back().second &&
subjectType->isEqual(conformingProto->getSelfInterfaceType()))
return;
assert(hasConformanceInSignature(sig, subjectType, conformingProto) &&
"missing explicit conformance in signature"); "missing explicit conformance in signature");
// Add the root of the path, which starts at this explicit requirement. // Add the root of the path, which starts at this explicit requirement.
path.path.push_back({subjectType, conformingProto}); path.path.push_back({subjectType, conformingProto});
}; };
buildPath(conforms->second, protocol); buildPath(this, conforms->second, protocol);
// Return the path; we're done! // Return the path; we're done!
return path; return path;

View File

@@ -49,9 +49,24 @@ func testPaths2<U: P2 & P4>(_ t: U) where U.AssocP3 == U.AssocP2.AssocP1 {
} }
func testPaths3<V: P5>(_ v: V) { func testPaths3<V: P5>(_ v: V) {
// CHECK: Conformance access path for V.AssocP3: P0 is V: P5 -> τ_0_0.AssocP3: Q0 -> τ_0_0: P0 // CHECK: Conformance access path for V.AssocP3: P0 is V: P5 -> Self.AssocP3: Q0 -> τ_0_0: P0
acceptP0(v.getAssocP3()) acceptP0(v.getAssocP3())
// CHECK: Conformance access path for V.AssocP3: Q0 is V: P5 -> τ_0_0.AssocP3: Q0 // CHECK: Conformance access path for V.AssocP3: Q0 is V: P5 -> Self.AssocP3: Q0
acceptQ0(v.getAssocP3()) acceptQ0(v.getAssocP3())
} }
protocol P6Unordered: P5Unordered {
associatedtype A: P0
}
protocol P5Unordered {
associatedtype A: P0
func getA() -> A
}
func testUnorderedP5_P6<W: P6Unordered>(_ w: W) {
// CHECK: Conformance access path for W.A: P0 is W: P6Unordered -> Self: P5Unordered -> τ_0_0.A: P0
acceptP0(w.getA())
}