Skip to content

Commit fa4a196

Browse files
authored
Merge pull request #7956 from DougGregor/conformance-access-path
2 parents 872581f + 202bc7e commit fa4a196

9 files changed

+341
-54
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);

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,11 @@ class GenericSignatureBuilder {
339339
///
340340
/// \returns true if this requirement makes the set of requirements
341341
/// inconsistent, in which case a diagnostic will have been issued.
342-
bool addRequirement(const Requirement &req, FloatingRequirementSource source);
342+
bool addRequirement(const Requirement &req, FloatingRequirementSource source,
343+
const SubstitutionMap *subMap = nullptr);
343344

344345
bool addRequirement(const Requirement &req, FloatingRequirementSource source,
346+
const SubstitutionMap *subMap,
345347
llvm::SmallPtrSetImpl<ProtocolDecl *> &Visited);
346348

347349
/// \brief Add a new requirement.
@@ -371,7 +373,7 @@ class GenericSignatureBuilder {
371373
/// where \c Dictionary requires that its key type be \c Hashable,
372374
/// the requirement \c K : Hashable is inferred from the parameter type,
373375
/// because the type \c Dictionary<K,V> cannot be formed without it.
374-
void inferRequirements(TypeLoc type);
376+
void inferRequirements(ModuleDecl &module, TypeLoc type);
375377

376378
/// Infer requirements from the given pattern, recursively.
377379
///
@@ -385,7 +387,8 @@ class GenericSignatureBuilder {
385387
/// where \c Dictionary requires that its key type be \c Hashable,
386388
/// the requirement \c K : Hashable is inferred from the parameter type,
387389
/// because the type \c Dictionary<K,V> cannot be formed without it.
388-
void inferRequirements(ParameterList *params,GenericParamList *genericParams);
390+
void inferRequirements(ModuleDecl &module, ParameterList *params,
391+
GenericParamList *genericParams);
389392

390393
/// Finalize the set of requirements, performing any remaining checking
391394
/// required before generating archetypes.
@@ -821,6 +824,9 @@ class GenericSignatureBuilder::RequirementSource final
821824
.dyn_cast<const RequirementRepr *>();
822825
}
823826

827+
/// Retrieve the type stored in this requirement.
828+
Type getStoredType() const;
829+
824830
/// Retrieve the protocol for this requirement, if there is one.
825831
ProtocolDecl *getProtocolDecl() const;
826832

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

0 commit comments

Comments
 (0)