Skip to content

Commit 202bc7e

Browse files
committed
[AST] Introduce GenericSignature::getConformanceAccessPath().
Introduce an API that determines the "conformance access path" that one would follow to find the conformance of a given type parameter (e.g., T.Iterator.Element) to a given protocol (e.g., Equatable). A conformance access path starts at one of the explicit requirements of that generic signature and then proceeds through zero or more protocol-supplied requirements. For example, given this function: func f<C: Collection>(_: C) { } The conformance access path for "C.Iterator: IteratorProtocol" is (C, Collection) -> (Self, Sequence) -> (Self.Iterator, IteratorProtocol) because one starts with the explicit requirement "C: Collection", goes to the inherited protocol requirement (the "Self" in Collection conforms to Sequence) and then a requirement on the associated type (Self.Iterator in Sequence conforms to IteratorProtocol). This is all scaffolding now; it's intended to be used by IRGen (to find the witness tables it needs) and SubstitutionMap (to dig out conformances during substitution).
1 parent cd93126 commit 202bc7e

File tree

4 files changed

+261
-2
lines changed

4 files changed

+261
-2
lines changed

include/swift/AST/GenericSignature.h

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include "swift/AST/SubstitutionList.h"
2222
#include "llvm/ADT/ArrayRef.h"
2323
#include "llvm/ADT/FoldingSet.h"
24+
#include "llvm/ADT/SmallVector.h"
2425
#include "llvm/Support/TrailingObjects.h"
26+
#include <utility>
2527

2628
namespace swift {
2729

@@ -31,6 +33,61 @@ class ProtocolType;
3133
class Substitution;
3234
class SubstitutionMap;
3335

36+
/// An access path used to find a particular protocol conformance within
37+
/// a generic signature.
38+
///
39+
/// One can follow a conformance path to extract any conformance that is
40+
/// derivable within the generic signature. For example, given:
41+
///
42+
/// \code
43+
/// func f<C: Collection>(_: C) where C.Iterator.Element: Hashable { }
44+
/// \endcode
45+
///
46+
/// One can extract conformances for various types and protocols, including
47+
/// those written directly (\c C: Collection, \c C.Iterator.Element: Hashable),
48+
/// and others that can be derived (\c C: Sequence,
49+
/// \c C.Iterator: IteratorProtocol, \c C.Iterator.Element: Equatable).
50+
///
51+
/// A conformance access path is a sequence of (dependent type, protocol decl)
52+
/// pairs that starts at an explicit requirement in the generic signature
53+
/// (e.g., \c C: Collection). Each subsequent step names a dependent
54+
/// type and protocol that refers to an explicit requirement in the requirement
55+
/// signature of the previous step's protocol. For example, consider the
56+
/// derived conformance \c C.Iterator: IteratorProtocol, which has the
57+
/// following path:
58+
///
59+
/// \code
60+
/// (C, Collection) -> (Self, Sequence) -> (Self.Iterator, IteratorProtocol)
61+
/// \endcode
62+
///
63+
/// Therefore, the path starts at \c C: Collection. It then retrieves the
64+
/// \c Sequence conformance of \c C (because \c Collection inherits
65+
/// \c Sequence). Finally, it extracts the conformance of the associated type
66+
/// \c Iterator to \c IteratorProtocol from the \c Sequence protocol.
67+
class ConformanceAccessPath {
68+
public:
69+
/// An entry in the conformance access path, which is described by the
70+
/// dependent type on which the conformance is stated as the protocol to
71+
/// which.
72+
typedef std::pair<Type, ProtocolDecl *> Entry;
73+
74+
private:
75+
llvm::SmallVector<Entry, 2> path;
76+
77+
friend class GenericSignature;
78+
79+
public:
80+
typedef llvm::SmallVector<Entry, 2>::const_iterator iterator;
81+
typedef llvm::SmallVector<Entry, 2>::const_iterator const_iterator;
82+
83+
const_iterator begin() const { return path.begin(); }
84+
const_iterator end() const { return path.end(); }
85+
86+
void print(raw_ostream &OS) const;
87+
88+
LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in a debugger");
89+
};
90+
3491
/// Describes the generic signature of a particular declaration, including
3592
/// both the generic type parameters and the requirements placed on those
3693
/// generic parameters.
@@ -90,8 +147,9 @@ class alignas(1 << TypeAlignInBits) GenericSignature final
90147

91148
/// Create a new generic signature with the given type parameters and
92149
/// requirements, first canonicalizing the types.
93-
static CanGenericSignature getCanonical(ArrayRef<GenericTypeParamType *> params,
94-
ArrayRef<Requirement> requirements);
150+
static CanGenericSignature getCanonical(
151+
ArrayRef<GenericTypeParamType *> params,
152+
ArrayRef<Requirement> requirements);
95153

96154
/// Retrieve the generic parameters.
97155
ArrayRef<GenericTypeParamType *> getGenericParams() const {
@@ -264,6 +322,22 @@ class alignas(1 << TypeAlignInBits) GenericSignature final
264322
CanType getCanonicalTypeInContext(Type type, GenericSignatureBuilder &builder);
265323
bool isCanonicalTypeInContext(Type type, GenericSignatureBuilder &builder);
266324

325+
/// Retrieve the conformance access path used to extract the conformance of
326+
/// interface \c type to the given \c protocol.
327+
///
328+
/// \param type The interface type whose conformance access path is to be
329+
/// queried.
330+
/// \param protocol A protocol to which \c type conforms.
331+
///
332+
/// \returns the conformance access path that starts at a requirement of
333+
/// this generic signature and ends at the conformance that makes \c type
334+
/// conform to \c protocol.
335+
///
336+
/// \seealso ConformanceAccessPath
337+
ConformanceAccessPath getConformanceAccessPath(Type type,
338+
ProtocolDecl *protocol,
339+
ModuleDecl &mod);
340+
267341
static void Profile(llvm::FoldingSetNodeID &ID,
268342
ArrayRef<GenericTypeParamType *> genericParams,
269343
ArrayRef<Requirement> requirements);

lib/AST/GenericSignature.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,26 @@
2121
#include "swift/AST/GenericEnvironment.h"
2222
#include "swift/AST/Module.h"
2323
#include "swift/AST/Types.h"
24+
#include "swift/Basic/STLExtras.h"
25+
#include <functional>
2426

2527
using namespace swift;
2628

29+
void ConformanceAccessPath::print(raw_ostream &out) const {
30+
interleave(begin(), end(),
31+
[&](const Entry &entry) {
32+
entry.first.print(out);
33+
out << ": " << entry.second->getName();
34+
}, [&] {
35+
out << " -> ";
36+
});
37+
}
38+
39+
void ConformanceAccessPath::dump() const {
40+
print(llvm::errs());
41+
llvm::errs() << "\n";
42+
}
43+
2744
GenericSignature::GenericSignature(ArrayRef<GenericTypeParamType *> params,
2845
ArrayRef<Requirement> requirements,
2946
bool isKnownCanonical)
@@ -719,6 +736,109 @@ GenericEnvironment *CanGenericSignature::getGenericEnvironment(
719736
module);
720737
}
721738

739+
ConformanceAccessPath GenericSignature::getConformanceAccessPath(
740+
Type type,
741+
ProtocolDecl *protocol,
742+
ModuleDecl &mod) {
743+
assert(type->isTypeParameter() && "not a type parameter");
744+
745+
// Resolve this type to a potential archetype.
746+
auto &builder = *getGenericSignatureBuilder(mod);
747+
auto pa = builder.resolveArchetype(type);
748+
auto rep = pa->getRepresentative();
749+
750+
// Dig out the conformance of this type to the given protocol, because we
751+
// want its requirement source.
752+
auto conforms = rep->getConformsTo().find(protocol);
753+
assert(conforms != rep->getConformsTo().end());
754+
755+
// Follow the requirement source to form the conformance access path.
756+
typedef GenericSignatureBuilder::RequirementSource RequirementSource;
757+
ConformanceAccessPath path;
758+
759+
#ifndef NDEBUG
760+
// Local function to determine whether there is a conformance of the given
761+
// subject type to the given protocol within the given generic signature's
762+
// explicit requirements.
763+
auto hasConformanceInSignature = [&](const GenericSignature *genericSig,
764+
Type subjectType,
765+
ProtocolDecl *proto) -> bool {
766+
// Make sure this requirement exists in the requirement signature.
767+
for (const auto& req: genericSig->getRequirements()) {
768+
if (req.getKind() == RequirementKind::Conformance &&
769+
req.getFirstType()->isEqual(subjectType) &&
770+
req.getSecondType()->castTo<ProtocolType>()->getDecl()
771+
== proto) {
772+
return true;
773+
}
774+
}
775+
776+
return false;
777+
};
778+
#endif
779+
780+
// Local function to construct the conformance access path from the
781+
// requirement
782+
std::function<void(const RequirementSource *, ProtocolDecl *)> buildPath;
783+
buildPath = [&](const RequirementSource *source,
784+
ProtocolDecl *conformingProto) {
785+
// Each protocol requirement is a step along the path.
786+
if (source->kind == RequirementSource::ProtocolRequirement) {
787+
// Follow the rest of the path to derive the conformance into which
788+
// this particular protocol requirement step would look.
789+
auto inProtocol = source->getProtocolDecl();
790+
buildPath(source->parent, inProtocol);
791+
792+
// Add this step along the path, which involes looking for the
793+
// conformance we want (\c conformingProto) within the protocol
794+
// described by this source.
795+
796+
// Canonicalize the subject type within the protocol's requirement
797+
// signature.
798+
Type subjectType = source->getStoredType();
799+
subjectType = inProtocol->getRequirementSignature()
800+
->getCanonicalTypeInContext(subjectType,
801+
*inProtocol->getParentModule());
802+
803+
assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
804+
subjectType, conformingProto) &&
805+
"missing explicit conformance in requirement signature");
806+
807+
// Record this step.
808+
path.path.push_back({subjectType, conformingProto});
809+
810+
// We're done.
811+
return;
812+
}
813+
814+
// If we still have a parent, keep going.
815+
if (source->parent) {
816+
buildPath(source->parent, conformingProto);
817+
return;
818+
}
819+
820+
// We are at an explicit or inferred requirement.
821+
assert(source->kind == RequirementSource::Explicit ||
822+
source->kind == RequirementSource::Inferred);
823+
824+
// Retrieve the subject type, which is the archetype anchor for the root
825+
// of this requirement.
826+
auto anchor =
827+
source->getRootPotentialArchetype()->getArchetypeAnchor(builder);
828+
Type subjectType = anchor->getDependentType(getGenericParams(), false);
829+
830+
assert(hasConformanceInSignature(this, subjectType, conformingProto) &&
831+
"missing explicit conformance in signature");
832+
833+
// Add the root of the path, which starts at this explicit requirement.
834+
path.path.push_back({subjectType, conformingProto});
835+
};
836+
buildPath(conforms->second, protocol);
837+
838+
// Return the path; we're done!
839+
return path;
840+
}
841+
722842
unsigned GenericParamKey::findIndexIn(
723843
llvm::ArrayRef<GenericTypeParamType *> genericParams) const {
724844
// For depth 0, we have random access. We perform the extra checking so that

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5121,6 +5121,27 @@ Optional<ProtocolConformanceRef> TypeChecker::conformsToProtocol(
51215121
sf->addUsedConformance(normalConf);
51225122
}
51235123
}
5124+
5125+
// Debugging aid: display the conformance access path for archetype
5126+
// conformances.
5127+
if (Context.LangOpts.DebugGenericSignatures && InExpression &&
5128+
T->is<ArchetypeType>() && lookupResult->isAbstract() &&
5129+
T->castTo<ArchetypeType>()->getGenericEnvironment()
5130+
== DC->getGenericEnvironmentOfContext()) {
5131+
auto interfaceType = DC->mapTypeOutOfContext(T);
5132+
if (interfaceType->isTypeParameter()) {
5133+
llvm::errs() << "Conformance access path for ";
5134+
T.print(llvm::errs());
5135+
llvm::errs() << ": " << Proto->getName() << " is ";
5136+
5137+
auto genericSig = DC->getGenericSignatureOfContext();
5138+
genericSig->getConformanceAccessPath(interfaceType, Proto,
5139+
*DC->getParentModule())
5140+
.print(llvm::errs());
5141+
llvm::errs() << "\n";
5142+
}
5143+
}
5144+
51245145
return lookupResult;
51255146
}
51265147

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-typecheck-verify-swift -typecheck %s -verify
2+
// RUN: %target-typecheck-verify-swift -typecheck -debug-generic-signatures %s > %t.dump 2>&1
3+
// RUN: %FileCheck %s < %t.dump
4+
5+
protocol P0 { }
6+
protocol Q0: P0 { }
7+
8+
protocol P1 {
9+
associatedtype AssocP1: Q0
10+
11+
func getAssocP1() -> AssocP1
12+
}
13+
14+
protocol P2 : P1 {
15+
associatedtype AssocP2: P1
16+
17+
func getAssocP2() -> AssocP2
18+
}
19+
20+
protocol P3 {
21+
associatedtype AssocP3: P0
22+
23+
func getAssocP3() -> AssocP3
24+
}
25+
26+
protocol P4: P3 { }
27+
28+
func acceptP0<T: P0>(_: T) { }
29+
func acceptP1<T: P1>(_: T) { }
30+
func acceptP2<T: P2>(_: T) { }
31+
func acceptP3<T: P3>(_: T) { }
32+
33+
34+
func testPaths1<T: P2 & P4>(_ t: T) {
35+
// CHECK: Conformance access path for T.AssocP2.AssocP1: P0 is T: P2 -> τ_0_0.AssocP2: P1 -> τ_0_0.AssocP1: Q0 -> τ_0_0: P0
36+
acceptP0(t.getAssocP2().getAssocP1())
37+
// CHECK: Conformance access path for T.AssocP3: P0 is T: P4 -> τ_0_0: P3 -> τ_0_0.AssocP3: P0
38+
acceptP0(t.getAssocP3())
39+
}
40+
41+
func testPaths2<U: P2 & P4>(_ t: U) where U.AssocP3 == U.AssocP2.AssocP1 {
42+
// CHECK: Conformance access path for U.AssocP3: P0 is U: P4 -> τ_0_0: P3 -> τ_0_0.AssocP3: P0
43+
acceptP0(t.getAssocP2().getAssocP1())
44+
}

0 commit comments

Comments
 (0)