Skip to content

[AST] Introduce GenericSignature::getConformanceAccessPath(). #7956

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 76 additions & 2 deletions include/swift/AST/GenericSignature.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
#include "swift/AST/SubstitutionList.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/TrailingObjects.h"
#include <utility>

namespace swift {

Expand All @@ -31,6 +33,61 @@ class ProtocolType;
class Substitution;
class SubstitutionMap;

/// An access path used to find a particular protocol conformance within
/// a generic signature.
///
/// One can follow a conformance path to extract any conformance that is
/// derivable within the generic signature. For example, given:
///
/// \code
/// func f<C: Collection>(_: C) where C.Iterator.Element: Hashable { }
/// \endcode
///
/// One can extract conformances for various types and protocols, including
/// those written directly (\c C: Collection, \c C.Iterator.Element: Hashable),
/// and others that can be derived (\c C: Sequence,
/// \c C.Iterator: IteratorProtocol, \c C.Iterator.Element: Equatable).
///
/// A conformance access path is a sequence of (dependent type, protocol decl)
/// pairs that starts at an explicit requirement in the generic signature
/// (e.g., \c C: Collection). Each subsequent step names a dependent
/// type and protocol that refers to an explicit requirement in the requirement
/// signature of the previous step's protocol. For example, consider the
/// derived conformance \c C.Iterator: IteratorProtocol, which has the
/// following path:
///
/// \code
/// (C, Collection) -> (Self, Sequence) -> (Self.Iterator, IteratorProtocol)
/// \endcode
///
/// Therefore, the path starts at \c C: Collection. It then retrieves the
/// \c Sequence conformance of \c C (because \c Collection inherits
/// \c Sequence). Finally, it extracts the conformance of the associated type
/// \c Iterator to \c IteratorProtocol from the \c Sequence protocol.
class ConformanceAccessPath {
public:
/// An entry in the conformance access path, which is described by the
/// dependent type on which the conformance is stated as the protocol to
/// which.
typedef std::pair<Type, ProtocolDecl *> Entry;

private:
llvm::SmallVector<Entry, 2> path;

friend class GenericSignature;

public:
typedef llvm::SmallVector<Entry, 2>::const_iterator iterator;
typedef llvm::SmallVector<Entry, 2>::const_iterator const_iterator;

const_iterator begin() const { return path.begin(); }
const_iterator end() const { return path.end(); }

void print(raw_ostream &OS) const;

LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in a debugger");
};

/// Describes the generic signature of a particular declaration, including
/// both the generic type parameters and the requirements placed on those
/// generic parameters.
Expand Down Expand Up @@ -90,8 +147,9 @@ class alignas(1 << TypeAlignInBits) GenericSignature final

/// Create a new generic signature with the given type parameters and
/// requirements, first canonicalizing the types.
static CanGenericSignature getCanonical(ArrayRef<GenericTypeParamType *> params,
ArrayRef<Requirement> requirements);
static CanGenericSignature getCanonical(
ArrayRef<GenericTypeParamType *> params,
ArrayRef<Requirement> requirements);

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

/// Retrieve the conformance access path used to extract the conformance of
/// interface \c type to the given \c protocol.
///
/// \param type The interface type whose conformance access path is to be
/// queried.
/// \param protocol A protocol to which \c type conforms.
///
/// \returns the conformance access path that starts at a requirement of
/// this generic signature and ends at the conformance that makes \c type
/// conform to \c protocol.
///
/// \seealso ConformanceAccessPath
ConformanceAccessPath getConformanceAccessPath(Type type,
ProtocolDecl *protocol,
ModuleDecl &mod);

static void Profile(llvm::FoldingSetNodeID &ID,
ArrayRef<GenericTypeParamType *> genericParams,
ArrayRef<Requirement> requirements);
Expand Down
12 changes: 9 additions & 3 deletions include/swift/AST/GenericSignatureBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,11 @@ class GenericSignatureBuilder {
///
/// \returns true if this requirement makes the set of requirements
/// inconsistent, in which case a diagnostic will have been issued.
bool addRequirement(const Requirement &req, FloatingRequirementSource source);
bool addRequirement(const Requirement &req, FloatingRequirementSource source,
const SubstitutionMap *subMap = nullptr);

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

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

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

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

/// Retrieve the type stored in this requirement.
Type getStoredType() const;

/// Retrieve the protocol for this requirement, if there is one.
ProtocolDecl *getProtocolDecl() const;

Expand Down
120 changes: 120 additions & 0 deletions lib/AST/GenericSignature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,26 @@
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Module.h"
#include "swift/AST/Types.h"
#include "swift/Basic/STLExtras.h"
#include <functional>

using namespace swift;

void ConformanceAccessPath::print(raw_ostream &out) const {
interleave(begin(), end(),
[&](const Entry &entry) {
entry.first.print(out);
out << ": " << entry.second->getName();
}, [&] {
out << " -> ";
});
}

void ConformanceAccessPath::dump() const {
print(llvm::errs());
llvm::errs() << "\n";
}

GenericSignature::GenericSignature(ArrayRef<GenericTypeParamType *> params,
ArrayRef<Requirement> requirements,
bool isKnownCanonical)
Expand Down Expand Up @@ -719,6 +736,109 @@ GenericEnvironment *CanGenericSignature::getGenericEnvironment(
module);
}

ConformanceAccessPath GenericSignature::getConformanceAccessPath(
Type type,
ProtocolDecl *protocol,
ModuleDecl &mod) {
assert(type->isTypeParameter() && "not a type parameter");

// Resolve this type to a potential archetype.
auto &builder = *getGenericSignatureBuilder(mod);
auto pa = builder.resolveArchetype(type);
auto rep = pa->getRepresentative();

// Dig out the conformance of this type to the given protocol, because we
// want its requirement source.
auto conforms = rep->getConformsTo().find(protocol);
assert(conforms != rep->getConformsTo().end());

// Follow the requirement source to form the conformance access path.
typedef GenericSignatureBuilder::RequirementSource RequirementSource;
ConformanceAccessPath path;

#ifndef NDEBUG
// Local function to determine whether there is a conformance of the given
// subject type to the given protocol within the given generic signature's
// explicit requirements.
auto hasConformanceInSignature = [&](const GenericSignature *genericSig,
Type subjectType,
ProtocolDecl *proto) -> bool {
// Make sure this requirement exists in the requirement signature.
for (const auto& req: genericSig->getRequirements()) {
if (req.getKind() == RequirementKind::Conformance &&
req.getFirstType()->isEqual(subjectType) &&
req.getSecondType()->castTo<ProtocolType>()->getDecl()
== proto) {
return true;
}
}

return false;
};
#endif

// Local function to construct the conformance access path from the
// requirement
std::function<void(const RequirementSource *, ProtocolDecl *)> buildPath;
buildPath = [&](const RequirementSource *source,
ProtocolDecl *conformingProto) {
// Each protocol requirement is a step along the path.
if (source->kind == RequirementSource::ProtocolRequirement) {
// Follow the rest of the path to derive the conformance into which
// this particular protocol requirement step would look.
auto inProtocol = source->getProtocolDecl();
buildPath(source->parent, inProtocol);

// 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
// signature.
Type subjectType = source->getStoredType();
subjectType = inProtocol->getRequirementSignature()
->getCanonicalTypeInContext(subjectType,
*inProtocol->getParentModule());

assert(hasConformanceInSignature(inProtocol->getRequirementSignature(),
subjectType, conformingProto) &&
"missing explicit conformance in requirement signature");

// Record this step.
path.path.push_back({subjectType, conformingProto});

// We're done.
return;
}

// If we still have a parent, keep going.
if (source->parent) {
buildPath(source->parent, conformingProto);
return;
}

// We are at an explicit or inferred requirement.
assert(source->kind == RequirementSource::Explicit ||
source->kind == RequirementSource::Inferred);

// Retrieve the subject type, which is the archetype anchor for the root
// of this requirement.
auto anchor =
source->getRootPotentialArchetype()->getArchetypeAnchor(builder);
Type subjectType = anchor->getDependentType(getGenericParams(), false);

assert(hasConformanceInSignature(this, subjectType, conformingProto) &&
"missing explicit conformance in signature");

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

// Return the path; we're done!
return path;
}

unsigned GenericParamKey::findIndexIn(
llvm::ArrayRef<GenericTypeParamType *> genericParams) const {
// For depth 0, we have random access. We perform the extra checking so that
Expand Down
Loading