Files
swift-mirror/include/swift/AST/GenericSignature.h
Slava Pestov e6ff771d59 GSB: Rewrite getConformanceAccessPath(), again
The new approach is to not look at RequirementSources at all. Instead,
we exhaustively enumerate all conformance access paths, beginning
from the root conformance requirements in the signature, then doing
all conformance requirements from those protocols' requirement
signatures, and so on.

We enumerate conformance access paths in breadth first order by
length until we find the one we want. The results are memoized.

This fixes a regression with another change I'm working on. The
test case does not fail with this PR alone, but I'm adding it now
anyway.
2021-05-06 17:55:43 -04:00

492 lines
18 KiB
C++

//===--- GenericSignature.h - Generic Signature AST -------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the GenericSignature class and its related classes.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_GENERIC_SIGNATURE_H
#define SWIFT_AST_GENERIC_SIGNATURE_H
#include "swift/AST/PrintOptions.h"
#include "swift/AST/Requirement.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeAlignments.h"
#include "swift/Basic/Debug.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 {
class GenericSignatureBuilder;
class ProtocolConformanceRef;
class ProtocolType;
class SubstitutionMap;
class GenericEnvironment;
/// 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:
ArrayRef<Entry> path;
ConformanceAccessPath(ArrayRef<Entry> path) : path(path) {}
friend class GenericSignatureImpl;
friend class GenericSignatureBuilder;
public:
typedef const Entry *const_iterator;
typedef const_iterator iterator;
const_iterator begin() const { return path.begin(); }
const_iterator end() const { return path.end(); }
const Entry &back() const { return path.back(); }
void print(raw_ostream &OS) const;
SWIFT_DEBUG_DUMP;
};
class CanGenericSignature;
class GenericSignatureImpl;
class GenericTypeParamType;
/// Describes the generic signature of a particular declaration, including
/// both the generic type parameters and the requirements placed on those
/// generic parameters.
class GenericSignature {
const GenericSignatureImpl *Ptr;
public:
/// Create a new generic signature with the given type parameters and
/// requirements.
static GenericSignature get(ArrayRef<GenericTypeParamType *> params,
ArrayRef<Requirement> requirements,
bool isKnownCanonical = false);
static GenericSignature get(TypeArrayView<GenericTypeParamType> params,
ArrayRef<Requirement> requirements,
bool isKnownCanonical = false);
public:
static ASTContext &getASTContext(TypeArrayView<GenericTypeParamType> params,
ArrayRef<Requirement> requirements);
public:
/*implicit*/ GenericSignature(const GenericSignatureImpl *P = 0) : Ptr(P) {}
const GenericSignatureImpl *getPointer() const { return Ptr; }
bool isNull() const { return Ptr == 0; }
const GenericSignatureImpl *operator->() const {
assert(Ptr && "Cannot dereference a null GenericSignature!");
return Ptr;
}
explicit operator bool() const { return Ptr != 0; }
/// Whether the given set of requirements involves a type variable.
static bool hasTypeVariable(ArrayRef<Requirement> requirements);
friend llvm::hash_code hash_value(GenericSignature sig) {
using llvm::hash_value;
return hash_value(sig.getPointer());
}
void print(raw_ostream &OS,
const PrintOptions &Options = PrintOptions()) const;
void print(ASTPrinter &Printer,
const PrintOptions &Opts = PrintOptions()) const;
SWIFT_DEBUG_DUMP;
std::string getAsString() const;
/// Returns the canonical generic signature, or \c nullptr if the underlying
/// pointer is \c nullptr. The result is cached.
CanGenericSignature getCanonicalSignature() const;
// Support for FoldingSet.
void Profile(llvm::FoldingSetNodeID &id) const;
static void Profile(llvm::FoldingSetNodeID &ID,
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements);
public:
using RequiredProtocols = SmallVector<ProtocolDecl *, 2>;
private:
// Direct comparison is disabled for generic signatures. Canonicalize them
// first, or use isEqual.
void operator==(GenericSignature T) const = delete;
void operator!=(GenericSignature T) const = delete;
};
/// A reference to a canonical generic signature.
class CanGenericSignature : public GenericSignature {
bool isActuallyCanonicalOrNull() const;
public:
/// Create a new generic signature with the given type parameters and
/// requirements, first canonicalizing the types.
static CanGenericSignature
getCanonical(TypeArrayView<GenericTypeParamType> params,
ArrayRef<Requirement> requirements);
public:
CanGenericSignature(std::nullptr_t) : GenericSignature(nullptr) {}
explicit CanGenericSignature(const GenericSignatureImpl *P = 0)
: GenericSignature(P) {
assert(isActuallyCanonicalOrNull() &&
"Forming a CanGenericSignature out of a non-canonical signature!");
}
explicit CanGenericSignature(GenericSignature S) : GenericSignature(S) {
assert(isActuallyCanonicalOrNull() &&
"Forming a CanGenericSignature out of a non-canonical signature!");
}
ArrayRef<CanTypeWrapper<GenericTypeParamType>> getGenericParams() const;
bool operator==(CanGenericSignature S) const {
return getPointer() == S.getPointer();
}
bool operator!=(CanGenericSignature S) const { return !operator==(S); }
};
/// The underlying implementation of generic signatures.
class alignas(1 << TypeAlignInBits) GenericSignatureImpl final
: public llvm::FoldingSetNode,
private llvm::TrailingObjects<GenericSignatureImpl, Type, Requirement> {
friend class ASTContext;
friend GenericSignature;
friend TrailingObjects;
GenericSignatureImpl(const GenericSignatureImpl&) = delete;
void operator=(const GenericSignatureImpl&) = delete;
const unsigned NumGenericParams;
const unsigned NumRequirements;
GenericEnvironment *GenericEnv = nullptr;
// Make vanilla new/delete illegal.
void *operator new(size_t Bytes) = delete;
void operator delete(void *Data) = delete;
size_t numTrailingObjects(OverloadToken<Type>) const {
return NumGenericParams;
}
size_t numTrailingObjects(OverloadToken<Requirement>) const {
return NumRequirements;
}
GenericSignatureImpl(TypeArrayView<GenericTypeParamType> params,
ArrayRef<Requirement> requirements,
bool isKnownCanonical);
// FIXME: Making this a CanGenericSignature reveals callers are violating
// the interface's invariants.
mutable llvm::PointerUnion<const GenericSignatureImpl *, ASTContext *>
CanonicalSignatureOrASTContext;
friend class ArchetypeType;
public:
/// Retrieve the generic parameters.
TypeArrayView<GenericTypeParamType> getGenericParams() const {
return TypeArrayView<GenericTypeParamType>(
{getTrailingObjects<Type>(), NumGenericParams});
}
/// Retrieve the innermost generic parameters.
///
/// Given a generic signature for a nested generic type, produce an
/// array of the generic parameters for the innermost generic type.
TypeArrayView<GenericTypeParamType> getInnermostGenericParams() const;
/// Retrieve the requirements.
ArrayRef<Requirement> getRequirements() const {
return {getTrailingObjects<Requirement>(), NumRequirements};
}
/// Only allow allocation by doing a placement new.
void *operator new(size_t Bytes, void *Mem) {
assert(Mem);
return Mem;
}
/// Look up a stored conformance in the generic signature. These are formed
/// from same-type constraints placed on associated types of generic
/// parameters which have conformance constraints on them.
ProtocolConformanceRef lookupConformance(CanType depTy,
ProtocolDecl *proto) const;
/// Iterate over all generic parameters, passing a flag to the callback
/// indicating if the generic parameter is canonical or not.
void forEachParam(
llvm::function_ref<void(GenericTypeParamType *, bool)> callback) const;
/// Check if the generic signature makes all generic parameters
/// concrete.
bool areAllParamsConcrete() const;
/// Compute the number of conformance requirements in this signature.
unsigned getNumConformanceRequirements() const {
unsigned result = 0;
for (const auto &req : getRequirements()) {
if (req.getKind() == RequirementKind::Conformance)
++result;
}
return result;
}
/// Return true if these two generic signatures are equal.
bool isEqual(GenericSignature Other) const;
/// Determines whether this GenericSignature is canonical.
bool isCanonical() const;
ASTContext &getASTContext() const;
/// Returns the canonical generic signature. The result is cached.
CanGenericSignature getCanonicalSignature() const;
/// Retrieve the generic signature builder for the given generic signature.
GenericSignatureBuilder *getGenericSignatureBuilder() const;
/// Returns the generic environment that provides fresh contextual types
/// (archetypes) that correspond to the interface types in this generic
/// signature.
GenericEnvironment *getGenericEnvironment() const;
/// Uniquing for the ASTContext.
void Profile(llvm::FoldingSetNodeID &ID) const {
Profile(ID, getGenericParams(), getRequirements());
}
/// Determine whether the given dependent type is required to be a class.
bool requiresClass(Type type) const;
/// Determine the superclass bound on the given dependent type.
Type getSuperclassBound(Type type) const;
/// Determine the set of protocols to which the given type parameter is
/// required to conform.
GenericSignature::RequiredProtocols getRequiredProtocols(Type type) const;
/// Determine whether the given type parameter is required to conform to
/// the given protocol.
bool requiresProtocol(Type type, ProtocolDecl *proto) const;
/// Determine whether the given dependent type is equal to a concrete type.
bool isConcreteType(Type type) const;
/// Return the concrete type that the given type parameter is constrained to,
/// or the null Type if it is not the subject of a concrete same-type
/// constraint.
Type getConcreteType(Type type) const;
/// Return the layout constraint that the given type parameter is constrained
/// to, or the null LayoutConstraint if it is not the subject of layout
/// constraint.
LayoutConstraint getLayoutConstraint(Type type) const;
/// Return whether two type parameters represent the same type under this
/// generic signature.
///
/// The type parameters must be known to not be concrete within the context.
bool areSameTypeParameterInContext(Type type1, Type type2) const;
bool areSameTypeParameterInContext(Type type1, Type type2,
GenericSignatureBuilder &builder) const;
/// Determine if \c sig can prove \c requirement, meaning that it can deduce
/// T: Foo or T == U (etc.) with the information it knows. This includes
/// checking against global state, if any/all of the types in the requirement
/// are concrete, not type parameters.
bool isRequirementSatisfied(Requirement requirement) const;
/// Return the requirements of this generic signature that are not also
/// satisfied by \c otherSig.
///
/// \param otherSig Another generic signature whose generic parameters are
/// equivalent to or a subset of the generic parameters in this signature.
SmallVector<Requirement, 4> requirementsNotSatisfiedBy(
GenericSignature otherSig) const;
/// Return the canonical version of the given type under this generic
/// signature.
CanType getCanonicalTypeInContext(Type type) const;
bool isCanonicalTypeInContext(Type type) const;
bool isCanonicalTypeInContext(Type type,
GenericSignatureBuilder &builder) const;
/// 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) const;
/// Get the ordinal of a generic parameter in this generic signature.
///
/// For example, if you have a generic signature for a nested context like:
/// <t_0_0, t_0_1, t_1_0>
/// then this will return 0 for t_0_0, 1 for t_0_1, and 2 for t_1_0.
unsigned getGenericParamOrdinal(GenericTypeParamType *param) const;
/// Get a substitution map that maps all of the generic signature's
/// generic parameters to themselves.
SubstitutionMap getIdentitySubstitutionMap() const;
/// Get the sugared form of a generic parameter type.
GenericTypeParamType *getSugaredType(GenericTypeParamType *type) const;
/// Get the sugared form of a type by substituting any
/// generic parameter types by their sugared form.
Type getSugaredType(Type type) const;
/// Whether this generic signature involves a type variable.
bool hasTypeVariable() const;
static void Profile(llvm::FoldingSetNodeID &ID,
TypeArrayView<GenericTypeParamType> genericParams,
ArrayRef<Requirement> requirements);
void print(raw_ostream &OS, PrintOptions Options = PrintOptions()) const;
void print(ASTPrinter &Printer, PrintOptions Opts = PrintOptions()) const;
SWIFT_DEBUG_DUMP;
std::string getAsString() const;
};
void simple_display(raw_ostream &out, GenericSignature sig);
inline bool CanGenericSignature::isActuallyCanonicalOrNull() const {
return getPointer() == nullptr ||
getPointer() ==
llvm::DenseMapInfo<GenericSignatureImpl *>::getEmptyKey() ||
getPointer() ==
llvm::DenseMapInfo<GenericSignatureImpl *>::getTombstoneKey() ||
getPointer()->isCanonical();
}
} // end namespace swift
namespace llvm {
static inline raw_ostream &operator<<(raw_ostream &OS,
swift::GenericSignature Sig) {
Sig.print(OS);
return OS;
}
// A GenericSignature casts like a GenericSignatureImpl*.
template <> struct simplify_type<const ::swift::GenericSignature> {
typedef const ::swift::GenericSignatureImpl *SimpleType;
static SimpleType getSimplifiedValue(const ::swift::GenericSignature &Val) {
return Val.getPointer();
}
};
template <>
struct simplify_type<::swift::GenericSignature>
: public simplify_type<const ::swift::GenericSignature> {};
template <> struct DenseMapInfo<swift::GenericSignature> {
static swift::GenericSignature getEmptyKey() {
return llvm::DenseMapInfo<swift::GenericSignatureImpl *>::getEmptyKey();
}
static swift::GenericSignature getTombstoneKey() {
return llvm::DenseMapInfo<swift::GenericSignatureImpl *>::getTombstoneKey();
}
static unsigned getHashValue(swift::GenericSignature Val) {
return DenseMapInfo<swift::GenericSignatureImpl *>::getHashValue(
Val.getPointer());
}
static bool isEqual(swift::GenericSignature LHS,
swift::GenericSignature RHS) {
return LHS.getPointer() == RHS.getPointer();
}
};
// A GenericSignature is "pointer like".
template <> struct PointerLikeTypeTraits<swift::GenericSignature> {
public:
static inline swift::GenericSignature getFromVoidPointer(void *P) {
return (swift::GenericSignatureImpl *)P;
}
static inline void *getAsVoidPointer(swift::GenericSignature S) {
return (void *)S.getPointer();
}
enum { NumLowBitsAvailable = swift::TypeAlignInBits };
};
template <> struct PointerLikeTypeTraits<swift::CanGenericSignature> {
public:
static inline swift::CanGenericSignature getFromVoidPointer(void *P) {
return swift::CanGenericSignature((swift::GenericSignatureImpl *)P);
}
static inline void *getAsVoidPointer(swift::CanGenericSignature S) {
return (void *)S.getPointer();
}
enum { NumLowBitsAvailable = swift::TypeAlignInBits };
};
} // end namespace llvm
#endif // SWIFT_AST_GENERIC_SIGNATURE_H