mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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) &&
|
||||||
|
|||||||
@@ -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,23 +780,31 @@ 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");
|
||||||
|
|
||||||
|
// If this step was computed via the requirement signature, add it
|
||||||
|
// directly.
|
||||||
|
if (source->usesRequirementSignature) {
|
||||||
// Add this step along the path, which involes looking for the
|
// Add this step along the path, which involes looking for the
|
||||||
// conformance we want (\c conformingProto) within the protocol
|
// conformance we want (\c conformingProto) within the protocol
|
||||||
// described by this source.
|
// described by this source.
|
||||||
|
|
||||||
// Canonicalize the subject type within the protocol's requirement
|
// Canonicalize the subject type within the protocol's requirement
|
||||||
// signature.
|
// signature.
|
||||||
|
// FIXME: We might be able to guarantee this when building from a
|
||||||
|
// requirement signature.
|
||||||
Type subjectType = source->getStoredType();
|
Type subjectType = source->getStoredType();
|
||||||
subjectType = inProtocol->getRequirementSignature()
|
subjectType = inProtocol->getRequirementSignature()
|
||||||
->getCanonicalTypeInContext(subjectType,
|
->getCanonicalTypeInContext(subjectType,
|
||||||
@@ -798,6 +816,38 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
|
|||||||
|
|
||||||
// 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;
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user