Skip to content

Commit 1f8b0f9

Browse files
committed
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.
1 parent 4cd76d9 commit 1f8b0f9

File tree

3 files changed

+106
-30
lines changed

3 files changed

+106
-30
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ class GenericSignatureBuilder::RequirementSource final
504504
WrittenRequirementLoc> {
505505

506506
friend class FloatingRequirementSource;
507+
friend class GenericSignature;
507508

508509
public:
509510
enum Kind : uint8_t {
@@ -584,6 +585,9 @@ class GenericSignatureBuilder::RequirementSource final
584585
/// Whether there is a trailing written requirement location.
585586
const bool hasTrailingWrittenRequirementLoc;
586587

588+
/// Whether a protocol requirement came from the requirement signature.
589+
const bool usesRequirementSignature;
590+
587591
/// The actual storage, described by \c storageKind.
588592
union {
589593
/// The root archetype.
@@ -673,7 +677,7 @@ class GenericSignatureBuilder::RequirementSource final
673677
WrittenRequirementLoc writtenReqLoc)
674678
: kind(kind), storageKind(StorageKind::RootArchetype),
675679
hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()),
676-
parent(nullptr) {
680+
usesRequirementSignature(false), parent(nullptr) {
677681
assert(isAcceptableStorageKind(kind, storageKind) &&
678682
"RequirementSource kind/storageKind mismatch");
679683

@@ -689,7 +693,7 @@ class GenericSignatureBuilder::RequirementSource final
689693
WrittenRequirementLoc writtenReqLoc)
690694
: kind(kind), storageKind(StorageKind::StoredType),
691695
hasTrailingWrittenRequirementLoc(!writtenReqLoc.isNull()),
692-
696+
usesRequirementSignature(protocol->isRequirementSignatureComputed()),
693697
parent(parent) {
694698
assert((static_cast<bool>(parent) != isRootKind(kind)) &&
695699
"Root RequirementSource should not have parent (or vice versa)");
@@ -707,7 +711,7 @@ class GenericSignatureBuilder::RequirementSource final
707711
ProtocolConformance *conformance)
708712
: kind(kind), storageKind(StorageKind::ProtocolConformance),
709713
hasTrailingWrittenRequirementLoc(false),
710-
parent(parent) {
714+
usesRequirementSignature(false), parent(parent) {
711715
assert((static_cast<bool>(parent) != isRootKind(kind)) &&
712716
"Root RequirementSource should not have parent (or vice versa)");
713717
assert(isAcceptableStorageKind(kind, storageKind) &&
@@ -720,7 +724,7 @@ class GenericSignatureBuilder::RequirementSource final
720724
AssociatedTypeDecl *assocType)
721725
: kind(kind), storageKind(StorageKind::AssociatedTypeDecl),
722726
hasTrailingWrittenRequirementLoc(false),
723-
parent(parent) {
727+
usesRequirementSignature(false), parent(parent) {
724728
assert((static_cast<bool>(parent) != isRootKind(kind)) &&
725729
"Root RequirementSource should not have parent (or vice versa)");
726730
assert(isAcceptableStorageKind(kind, storageKind) &&

lib/AST/GenericSignature.cpp

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,16 @@ GenericEnvironment *CanGenericSignature::getGenericEnvironment(
728728
module);
729729
}
730730

731+
/// Remove all of the associated type declarations from the given type
732+
/// parameter, producing \c DependentMemberTypes with names alone.
733+
static Type eraseAssociatedTypes(Type type) {
734+
if (auto depMemTy = type->getAs<DependentMemberType>())
735+
return DependentMemberType::get(eraseAssociatedTypes(depMemTy->getBase()),
736+
depMemTy->getName());
737+
738+
return type;
739+
}
740+
731741
ConformanceAccessPath GenericSignature::getConformanceAccessPath(
732742
Type type,
733743
ProtocolDecl *protocol,
@@ -770,42 +780,82 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
770780
#endif
771781

772782
// Local function to construct the conformance access path from the
773-
// requirement
774-
std::function<void(const RequirementSource *, ProtocolDecl *)> buildPath;
775-
buildPath = [&](const RequirementSource *source,
783+
// requirement.
784+
std::function<void(GenericSignature *, const RequirementSource *,
785+
ProtocolDecl *)> buildPath;
786+
buildPath = [&](GenericSignature *sig, const RequirementSource *source,
776787
ProtocolDecl *conformingProto) {
777788
// Each protocol requirement is a step along the path.
778789
if (source->kind == RequirementSource::ProtocolRequirement) {
779790
// Follow the rest of the path to derive the conformance into which
780791
// this particular protocol requirement step would look.
781792
auto inProtocol = source->getProtocolDecl();
782-
buildPath(source->parent, inProtocol);
793+
buildPath(sig, source->parent, inProtocol);
794+
assert(path.path.back().second == inProtocol &&
795+
"path produces incorrect conformance");
796+
797+
// If this step was computed via the requirement signature, add it
798+
// directly.
799+
if (source->usesRequirementSignature) {
800+
// Add this step along the path, which involes looking for the
801+
// conformance we want (\c conformingProto) within the protocol
802+
// described by this source.
803+
804+
// Canonicalize the subject type within the protocol's requirement
805+
// signature.
806+
// FIXME: We might be able to guarantee this when building from a
807+
// requirement signature.
808+
Type subjectType = source->getStoredType();
809+
subjectType = inProtocol->getRequirementSignature()
810+
->getCanonicalTypeInContext(subjectType,
811+
*inProtocol->getParentModule());
812+
813+
assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
814+
subjectType, conformingProto) &&
815+
"missing explicit conformance in requirement signature");
816+
817+
// Record this step.
818+
path.path.push_back({subjectType, conformingProto});
819+
return;
820+
}
783821

784-
// Add this step along the path, which involes looking for the
785-
// conformance we want (\c conformingProto) within the protocol
786-
// described by this source.
822+
// Canonicalize this step with respect to the requirement signature.
823+
if (!inProtocol->isRequirementSignatureComputed()) {
824+
inProtocol->computeRequirementSignature();
825+
assert(inProtocol->isRequirementSignatureComputed() &&
826+
"missing signature");
827+
}
828+
829+
// Get a generic signature builder for the requirement signature. This has
830+
// the requirement we need.
831+
auto reqSig = inProtocol->getRequirementSignature();
832+
auto &reqSigBuilder = *reqSig->getGenericSignatureBuilder(
833+
*inProtocol->getModuleContext());
787834

788-
// Canonicalize the subject type within the protocol's requirement
789-
// signature.
790-
Type subjectType = source->getStoredType();
791-
subjectType = inProtocol->getRequirementSignature()
792-
->getCanonicalTypeInContext(subjectType,
793-
*inProtocol->getParentModule());
835+
// Retrieve the stored type, but erase all of the specific associated
836+
// type declarations; we don't want any details of the enclosing context
837+
// to sneak in here.
838+
Type storedType = eraseAssociatedTypes(source->getStoredType());
794839

795-
assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
796-
subjectType, conformingProto) &&
797-
"missing explicit conformance in requirement signature");
840+
// Dig out the potential archetype for this stored type.
841+
auto pa = reqSigBuilder.resolveArchetype(storedType);
842+
auto rep = pa->getRepresentative();
798843

799-
// Record this step.
800-
path.path.push_back({subjectType, conformingProto});
844+
// Find the conformance of this potential archetype to the protocol in
845+
// question.
846+
auto knownConforms = rep->getConformsTo().find(conformingProto);
847+
assert(knownConforms != rep->getConformsTo().end());
848+
849+
// Build the path according to the requirement signature.
850+
buildPath(reqSig, knownConforms->second, conformingProto);
801851

802852
// We're done.
803853
return;
804854
}
805855

806856
// If we still have a parent, keep going.
807857
if (source->parent) {
808-
buildPath(source->parent, conformingProto);
858+
buildPath(sig, source->parent, conformingProto);
809859
return;
810860
}
811861

@@ -815,17 +865,24 @@ ConformanceAccessPath GenericSignature::getConformanceAccessPath(
815865

816866
// Retrieve the subject type, which is the archetype anchor for the root
817867
// of this requirement.
818-
auto anchor =
819-
source->getRootPotentialArchetype()->getArchetypeAnchor(builder);
820-
Type subjectType = anchor->getDependentType(getGenericParams(), false);
868+
auto subjectPA = source->getRootPotentialArchetype();
869+
subjectPA = subjectPA->getArchetypeAnchor(*subjectPA->getBuilder());
870+
Type subjectType = subjectPA->getDependentType(sig->getGenericParams(),
871+
false);
872+
873+
// Skip trivial path elements. These occur when querying a requirement
874+
// signature.
875+
if (!path.path.empty() && conformingProto == path.path.back().second &&
876+
subjectType->isEqual(conformingProto->getSelfInterfaceType()))
877+
return;
821878

822-
assert(hasConformanceInSignature(this, subjectType, conformingProto) &&
879+
assert(hasConformanceInSignature(sig, subjectType, conformingProto) &&
823880
"missing explicit conformance in signature");
824881

825882
// Add the root of the path, which starts at this explicit requirement.
826883
path.path.push_back({subjectType, conformingProto});
827884
};
828-
buildPath(conforms->second, protocol);
885+
buildPath(this, conforms->second, protocol);
829886

830887
// Return the path; we're done!
831888
return path;

test/Generics/conformance_access_path.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,24 @@ func testPaths2<U: P2 & P4>(_ t: U) where U.AssocP3 == U.AssocP2.AssocP1 {
4949
}
5050

5151
func testPaths3<V: P5>(_ v: V) {
52-
// CHECK: Conformance access path for V.AssocP3: P0 is V: P5 -> τ_0_0.AssocP3: Q0 -> τ_0_0: P0
52+
// CHECK: Conformance access path for V.AssocP3: P0 is V: P5 -> Self.AssocP3: Q0 -> τ_0_0: P0
5353
acceptP0(v.getAssocP3())
5454

55-
// CHECK: Conformance access path for V.AssocP3: Q0 is V: P5 -> τ_0_0.AssocP3: Q0
55+
// CHECK: Conformance access path for V.AssocP3: Q0 is V: P5 -> Self.AssocP3: Q0
5656
acceptQ0(v.getAssocP3())
5757
}
58+
59+
protocol P6Unordered: P5Unordered {
60+
associatedtype A: P0
61+
}
62+
63+
protocol P5Unordered {
64+
associatedtype A: P0
65+
66+
func getA() -> A
67+
}
68+
69+
func testUnorderedP5_P6<W: P6Unordered>(_ w: W) {
70+
// CHECK: Conformance access path for W.A: P0 is W: P6Unordered -> Self: P5Unordered -> τ_0_0.A: P0
71+
acceptP0(w.getA())
72+
}

0 commit comments

Comments
 (0)