blob: 2d1b01586a2ed421490b62fe1ea4f4921bf4d559 [file] [log] [blame]
//===--- ConstraintLocator.h - Constraint Locator ---------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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 provides the \c ConstraintLocator class and its related types,
// which is used by the constraint-based type checker to describe how
// a particular constraint was derived.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_CONSTRAINTLOCATOR_H
#define SWIFT_SEMA_CONSTRAINTLOCATOR_H
#include "swift/Basic/Debug.h"
#include "swift/Basic/LLVM.h"
#include "swift/AST/ASTNode.h"
#include "swift/AST/Type.h"
#include "swift/AST/Types.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ErrorHandling.h"
#include <utility>
namespace swift {
class Expr;
class TypeLoc;
class VarDecl;
class Pattern;
class SourceManager;
namespace constraints {
class ConstraintSystem;
/// Locates a given constraint within the expression being
/// type-checked, which may refer down into subexpressions and parts of
/// the types of those subexpressions.
///
/// Each locator as anchored at some expression, e.g., (3, (x, 3.14)),
/// and contains a path that digs further into the type of that expression.
/// For example, the path "tuple element #1" -> "tuple element #0" with the
/// above expression would refer to 'x'. If 'x' had function type, the
/// path could be further extended with either "-> argument" or "-> result",
/// to indicate constraints on its argument or result type.
class ConstraintLocator : public llvm::FoldingSetNode {
public:
/// Describes the kind of a particular path element, e.g.,
/// "tuple element", "call result", "base of member lookup", etc.
enum PathElementKind : unsigned char {
#define LOCATOR_PATH_ELT(Name) Name,
#define ABSTRACT_LOCATOR_PATH_ELT(Name)
#include "ConstraintLocatorPathElts.def"
};
/// Flags for efficiently recording certain information about a path.
/// All of this information should be re-derivable from the path.
///
/// Values are chosen so that an empty path has value 0 and the
/// flags for a concatenated paths is simply the bitwise-or of the
/// flags of the component paths.
enum Flag : unsigned {
/// Does this path involve a function conversion, i.e. a
/// FunctionArgument or FunctionResult node?
IsFunctionConversion = 0x1,
/// Does this path involve an argument being applied to a non-ephemeral
/// parameter?
IsNonEphemeralParam = 0x2,
};
/// One element in the path of a locator, which can include both
/// a kind (PathElementKind) and a value used to describe specific
/// kinds further (e.g., the position of a tuple element).
class PathElement {
PathElementKind kind;
/// The storage for the path element value. The value stores can either
/// be a pointer or an unsigned int. Only custom path elements store values.
uint64_t storage;
public:
#define LOCATOR_PATH_ELT(Name) class Name;
#include "ConstraintLocatorPathElts.def"
PathElement(PathElementKind kind, uint64_t storage = 0)
: kind(kind), storage(storage) {}
/// Retrieve the kind of path element.
PathElementKind getKind() const { return kind; }
/// Retrieve the raw storage value.
uint64_t getRawStorage() const { return storage; }
/// Attempts to cast the path element to a specific \c LocatorPathElt
/// subclass, returning \c None if unsuccessful.
template <class T>
Optional<T> getAs() const {
if (auto *result = dyn_cast<T>(this))
return *result;
return None;
}
/// Cast the path element to a specific \c LocatorPathElt subclass.
template <class T>
T castTo() const { return *cast<T>(this); }
/// Checks whether the path element is a specific \c LocatorPathElt
/// subclass.
template <class T>
bool is() const { return isa<T>(this); }
/// Return the summary flags for this particular element.
unsigned getNewSummaryFlags() const;
bool isConditionalRequirement() const {
return getKind() == PathElementKind::ConditionalRequirement;
}
bool isKeyPathDynamicMember() const {
return getKind() == PathElementKind::KeyPathDynamicMember;
}
bool isKeyPathComponent() const {
return getKind() == PathElementKind::KeyPathComponent;
}
bool isClosureResult() const {
return getKind() == PathElementKind::ClosureResult;
}
};
/// Return the summary flags for an entire path.
static unsigned getSummaryFlagsForPath(ArrayRef<PathElement> path) {
unsigned flags = 0;
for (auto &elt : path) flags |= elt.getNewSummaryFlags();
return flags;
}
/// Retrieve the expression that anchors this locator.
ASTNode getAnchor() const { return anchor; }
/// Retrieve the path that extends from the anchor to a specific
/// subcomponent.
ArrayRef<PathElement> getPath() const {
// FIXME: Alignment.
return llvm::makeArrayRef(reinterpret_cast<const PathElement *>(this + 1),
numPathElements);
}
unsigned getSummaryFlags() const { return summaryFlags; }
/// Determines whether this locator is part of a function
/// conversion.
bool isFunctionConversion() const {
return (getSummaryFlags() & IsFunctionConversion);
}
/// Checks whether this locator is describing an argument application for a
/// non-ephemeral parameter.
bool isNonEphemeralParameterApplication() const {
return (getSummaryFlags() & IsNonEphemeralParam);
}
/// Determine whether given locator points to the subscript reference
/// e.g. `foo[0]` or `\Foo.[0]`
bool isSubscriptMemberRef() const;
/// Determine whether give locator points to the type of the
/// key path expression.
bool isKeyPathType() const;
/// Determine whether given locator points to the keypath root
bool isKeyPathRoot() const;
/// Determine whether given locator points to the keypath value
bool isKeyPathValue() const;
/// Determine whether given locator points to the choice picked as
/// as result of the key path dynamic member lookup operation.
bool isResultOfKeyPathDynamicMemberLookup() const;
/// Determine whether this locator points to a subscript component
/// of the key path at some index.
bool isKeyPathSubscriptComponent() const;
/// Determine whether this locator points to the member found
/// via key path dynamic member lookup.
bool isForKeyPathDynamicMemberLookup() const;
/// Determine whether this locator points to one of the key path
/// components.
bool isForKeyPathComponent() const;
/// Determine whether this locator points to the generic parameter.
bool isForGenericParameter() const;
/// Determine whether this locator points to the element type of a
/// sequence in a for ... in ... loop.
bool isForSequenceElementType() const;
/// Determine whether this locator points to the contextual type.
bool isForContextualType() const;
/// Determine whether this locator points to the assignment expression.
bool isForAssignment() const;
/// Determine whether this locator points to the coercion expression.
bool isForCoercion() const;
/// Determine whether this locator points to the `try?` expression.
bool isForOptionalTry() const;
/// Determine whether this locator is for a result builder body result type.
bool isForResultBuilderBodyResult() const;
/// Determine whether this locator points directly to a given expression.
template <typename E> bool directlyAt() const {
if (auto *expr = getAnchor().dyn_cast<Expr *>())
return isa<E>(expr) && getPath().empty();
return false;
}
/// Check whether the first element in the path of this locator (if any)
/// is a given \c LocatorPathElt subclass.
template <class T>
bool isFirstElement() const {
auto path = getPath();
return !path.empty() && path.front().is<T>();
}
/// Attempts to cast the first path element of the locator to a specific
/// \c LocatorPathElt subclass, returning \c None if either unsuccessful or
/// the locator has no path elements.
template <class T>
Optional<T> getFirstElementAs() const {
auto path = getPath();
if (path.empty())
return None;
return path[0].getAs<T>();
}
/// Casts the first path element of the locator to a specific
/// \c LocatorPathElt subclass, asserting that it has at least one element.
template <class T>
T castFirstElementTo() const {
auto path = getPath();
assert(!path.empty() && "Expected at least one path element!");
return path[0].castTo<T>();
}
/// Check whether the last element in the path of this locator (if any)
/// is a given \c LocatorPathElt subclass.
template <class T>
bool isLastElement() const {
auto path = getPath();
return !path.empty() && path.back().is<T>();
}
/// Attempts to cast the last path element of the locator to a specific
/// \c LocatorPathElt subclass, returning \c None if either unsuccessful or
/// the locator has no path elements.
template <class T>
Optional<T> getLastElementAs() const {
auto path = getPath();
if (path.empty())
return None;
return path.back().getAs<T>();
}
/// Casts the last path element of the locator to a specific \c LocatorPathElt
/// subclass, asserting that it has at least one element.
template <class T>
T castLastElementTo() const {
auto path = getPath();
assert(!path.empty() && "Expected at least one path element!");
return path.back().castTo<T>();
}
using PathIterator = ArrayRef<PathElement>::iterator;
using PathReverseIterator = ArrayRef<PathElement>::reverse_iterator;
/// Attempts to find the first element in the locator's path that is a
/// specific \c LocatorPathElt subclass, returning \c None if no such element
/// exists.
///
/// \param iter A reference to an iterator which will be used to iterate
/// over the locator's path.
template <class T>
Optional<T> findFirst(PathIterator &iter) const {
auto path = getPath();
auto end = path.end();
assert(iter >= path.begin() && iter <= end);
for (; iter != end; ++iter)
if (auto elt = iter->getAs<T>())
return elt;
return None;
}
/// Attempts to find the first element in the locator's path that is a
/// specific \c LocatorPathElt subclass, returning \c None if no such element
/// exists.
template <class T>
Optional<T> findFirst() const {
auto iter = getPath().begin();
return findFirst<T>(iter);
}
/// Attempts to find the last element in the locator's path that is a
/// specific \c LocatorPathElt subclass, returning \c None if no such element
/// exists.
///
/// \param iter A reference to a reverse iterator which will be used to
/// iterate over the locator's path.
template <class T>
Optional<T> findLast(PathReverseIterator &iter) const {
auto path = getPath();
auto end = path.rend();
assert(iter >= path.rbegin() && iter <= end);
for (; iter != end; ++iter)
if (auto elt = iter->getAs<T>())
return elt;
return None;
}
/// Attempts to find the last element in the locator's path that is a
/// specific \c LocatorPathElt subclass, returning \c None if no such element
/// exists.
template <class T>
Optional<T> findLast() const {
auto iter = getPath().rbegin();
return findLast<T>(iter);
}
/// If this locator points to generic parameter return its type.
GenericTypeParamType *getGenericParameter() const;
/// Produce a profile of this locator, for use in a folding set.
static void Profile(llvm::FoldingSetNodeID &id, ASTNode anchor,
ArrayRef<PathElement> path);
/// Produce a profile of this locator, for use in a folding set.
void Profile(llvm::FoldingSetNodeID &id) {
Profile(id, anchor, getPath());
}
/// Produce a debugging dump of this locator.
SWIFT_DEBUG_DUMPER(dump(SourceManager *SM));
SWIFT_DEBUG_DUMPER(dump(ConstraintSystem *CS));
void dump(SourceManager *SM, raw_ostream &OS) const LLVM_ATTRIBUTE_USED;
private:
/// Initialize a constraint locator with an anchor and a path.
ConstraintLocator(ASTNode anchor, ArrayRef<PathElement> path, unsigned flags)
: anchor(anchor), numPathElements(path.size()), summaryFlags(flags) {
// FIXME: Alignment.
std::copy(path.begin(), path.end(),
reinterpret_cast<PathElement *>(this + 1));
}
/// Create a new locator from an anchor and an array of path
/// elements.
///
/// Note that this routine only handles the allocation and initialization
/// of the locator. The ConstraintSystem object is responsible for
/// uniquing via the FoldingSet.
static ConstraintLocator *create(llvm::BumpPtrAllocator &allocator,
ASTNode anchor, ArrayRef<PathElement> path,
unsigned flags) {
// FIXME: Alignment.
unsigned size = sizeof(ConstraintLocator)
+ path.size() * sizeof(PathElement);
void *mem = allocator.Allocate(size, alignof(ConstraintLocator));
return new (mem) ConstraintLocator(anchor, path, flags);
}
/// The expression at which this locator is anchored.
ASTNode anchor;
/// The number of path elements in this locator.
///
/// The actual path elements are stored after the locator.
unsigned numPathElements : 24;
/// A set of flags summarizing interesting properties of the path.
unsigned summaryFlags : 7;
friend class ConstraintSystem;
};
using LocatorPathElt = ConstraintLocator::PathElement;
// Disallow direct uses of isa/cast/dyn_cast on LocatorPathElt in favor of using
// is/castTo/getAs. This allows us to work with Optional<T> rather than pointers
// for getAs, and maintains consistency with ConstraintLocator's
// isLastElement/castLastElementTo/getLastElementAs members.
template <class X>
inline bool
isa(const LocatorPathElt &) = delete; // Use LocatorPathElt::is instead.
template <class X>
inline typename llvm::cast_retty<X, LocatorPathElt>::ret_type
cast(const LocatorPathElt &) = delete; // Use LocatorPathElt::castTo instead.
template <class X>
inline typename llvm::cast_retty<X, LocatorPathElt>::ret_type
dyn_cast(const LocatorPathElt &) = delete; // Use LocatorPathElt::getAs instead.
#define SIMPLE_LOCATOR_PATH_ELT(Name) \
class LocatorPathElt:: Name final : public LocatorPathElt { \
public: \
Name () : LocatorPathElt(ConstraintLocator:: Name) {} \
\
static bool classof(const LocatorPathElt *elt) { \
return elt->getKind() == ConstraintLocator:: Name; \
} \
};
#include "ConstraintLocatorPathElts.def"
/// A base class for custom path elements that store numeric values.
template <unsigned NumValues>
class StoredIntegerElement: public LocatorPathElt {
public:
template <unsigned NumNumericInputs = NumValues,
typename = typename std::enable_if<NumNumericInputs == 1>::type>
StoredIntegerElement(ConstraintLocator::PathElementKind kind, unsigned value)
: LocatorPathElt(kind, value) {
assert(value == getValue<0>() && "value truncated");
}
template <unsigned NumNumericInputs = NumValues,
typename = typename std::enable_if<NumNumericInputs == 2>::type>
StoredIntegerElement(ConstraintLocator::PathElementKind kind, unsigned value0, unsigned value1)
: LocatorPathElt(kind, (value0 << 16 | value1)) {
assert(value0 == getValue<0>() && "value0 truncated");
assert(value1 == getValue<1>() && "value1 truncated");
}
template <unsigned NumNumericInputs = NumValues,
typename = typename std::enable_if<NumNumericInputs == 3>::type>
StoredIntegerElement(ConstraintLocator::PathElementKind kind, unsigned value0,
unsigned value1, unsigned value2)
: LocatorPathElt(kind, uint64_t(value0) << 32 | uint64_t(value1) << 16 | uint64_t(value2)) {
assert(value0 == getValue<0>() && "value0 truncated");
assert(value1 == getValue<1>() && "value1 truncated");
assert(value2 == getValue<2>() && "value2 truncated");
}
/// Retrieve a value associated with the path element.
template <unsigned Index = 0,
typename = typename std::enable_if<(Index < NumValues)>::type>
unsigned getValue() const {
// We pack values into 16 bit components of the storage, with value0
// being stored in the upper bits, valueN in the lower bits. Therefore we
// need to shift out any extra values in the lower bits.
auto extraValues = NumValues - Index - 1;
auto value = getRawStorage() >> (extraValues * 16);
return value & 0xFFFF;
}
};
/// A base class for custom path elements that store a pointer.
template <typename T>
class StoredPointerElement: public LocatorPathElt {
public:
StoredPointerElement(ConstraintLocator::PathElementKind kind, const T *ptr)
: LocatorPathElt(kind, reinterpret_cast<uintptr_t>(ptr)) {
assert(ptr == getStoredPointer());
}
/// Retrieve the associated pointer for the element.
T *getStoredPointer() const { return reinterpret_cast<T *>(getRawStorage()); }
};
// The following LocatorPathElt subclasses are used to expose accessors for
// specific path element information. They shouldn't introduce additional
// storage, as LocatorPathElt gets passed about by value. Custom path elements
// should subclass StoredIntegerElement to store unsigned values, or StoredPointerElement
// to store pointer values.
class LocatorPathElt::ApplyArgToParam final : public StoredIntegerElement<3> {
public:
ApplyArgToParam(unsigned argIdx, unsigned paramIdx, ParameterTypeFlags flags)
: StoredIntegerElement(ConstraintLocator::ApplyArgToParam, argIdx, paramIdx, flags.toRaw()) {}
unsigned getArgIdx() const { return getValue<0>(); }
unsigned getParamIdx() const { return getValue<1>(); }
ParameterTypeFlags getParameterFlags() const {
return ParameterTypeFlags::fromRaw(getValue<2>());
}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::ApplyArgToParam;
}
};
class LocatorPathElt::SynthesizedArgument final : public StoredIntegerElement<1> {
public:
SynthesizedArgument(unsigned index)
: StoredIntegerElement(ConstraintLocator::SynthesizedArgument, index) {}
unsigned getIndex() const { return getValue(); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::SynthesizedArgument;
}
};
/// Abstract superclass for any kind of tuple element.
class LocatorPathElt::AnyTupleElement : public StoredIntegerElement<1> {
protected:
AnyTupleElement(PathElementKind kind, unsigned index)
: StoredIntegerElement(kind, index) {
assert(classof(this) && "classof needs updating");
}
public:
unsigned getIndex() const { return getValue(); }
static bool classof(const LocatorPathElt *elt) {
return elt->is<LocatorPathElt::TupleElement>() ||
elt->is<LocatorPathElt::NamedTupleElement>();
}
};
class LocatorPathElt::TupleElement final
: public LocatorPathElt::AnyTupleElement {
public:
TupleElement(unsigned index)
: AnyTupleElement(ConstraintLocator::TupleElement, index) {}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::TupleElement;
}
};
class LocatorPathElt::NamedTupleElement final
: public LocatorPathElt::AnyTupleElement {
public:
NamedTupleElement(unsigned index)
: AnyTupleElement(ConstraintLocator::NamedTupleElement, index) {}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::NamedTupleElement;
}
};
class LocatorPathElt::KeyPathComponent final : public StoredIntegerElement<1> {
public:
KeyPathComponent(unsigned index)
: StoredIntegerElement(ConstraintLocator::KeyPathComponent, index) {}
unsigned getIndex() const { return getValue(); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::KeyPathComponent;
}
};
class LocatorPathElt::GenericArgument final : public StoredIntegerElement<1> {
public:
GenericArgument(unsigned index)
: StoredIntegerElement(ConstraintLocator::GenericArgument, index) {}
unsigned getIndex() const { return getValue(); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::GenericArgument;
}
};
/// Abstract superclass for any kind of element that describes a requirement
/// placed on a type within a requirements clause.
class LocatorPathElt::AnyRequirement : public StoredIntegerElement<2> {
protected:
AnyRequirement(PathElementKind kind, unsigned index, RequirementKind reqKind)
: StoredIntegerElement(kind, index, static_cast<unsigned>(reqKind)) {
assert(classof(this) && "classof needs updating");
}
public:
unsigned getIndex() const { return getValue<0>(); }
RequirementKind getRequirementKind() const {
return static_cast<RequirementKind>(getValue<1>());
}
static bool classof(const LocatorPathElt *elt) {
return elt->is<LocatorPathElt::ConditionalRequirement>() ||
elt->is<LocatorPathElt::TypeParameterRequirement>();
}
};
class LocatorPathElt::ConditionalRequirement final
: public LocatorPathElt::AnyRequirement {
public:
ConditionalRequirement(unsigned index, RequirementKind reqKind)
: AnyRequirement(ConstraintLocator::ConditionalRequirement, index,
reqKind) {}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::ConditionalRequirement;
}
};
class LocatorPathElt::TypeParameterRequirement final
: public LocatorPathElt::AnyRequirement {
public:
TypeParameterRequirement(unsigned index, RequirementKind reqKind)
: AnyRequirement(ConstraintLocator::TypeParameterRequirement, index,
reqKind) {}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::TypeParameterRequirement;
}
};
class LocatorPathElt::ClosureBody final : public StoredIntegerElement<1> {
public:
ClosureBody(bool hasExplicitReturn = false)
: StoredIntegerElement(ConstraintLocator::ClosureBody, hasExplicitReturn) {}
/// Indicates whether body of the closure has any `return` statements.
bool hasExplicitReturn() const { return bool(getValue()); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::ClosureBody;
}
};
class LocatorPathElt::Witness final : public StoredPointerElement<ValueDecl> {
public:
Witness(ValueDecl *witness)
: StoredPointerElement(PathElementKind::Witness, witness) {}
ValueDecl *getDecl() const { return getStoredPointer(); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == PathElementKind::Witness;
}
};
class LocatorPathElt::ProtocolRequirement final : public StoredPointerElement<ValueDecl> {
public:
ProtocolRequirement(ValueDecl *decl)
: StoredPointerElement(PathElementKind::ProtocolRequirement, decl) {}
ValueDecl *getDecl() const { return getStoredPointer(); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == PathElementKind::ProtocolRequirement;
}
};
class LocatorPathElt::GenericParameter final : public StoredPointerElement<GenericTypeParamType> {
public:
GenericParameter(GenericTypeParamType *type)
: StoredPointerElement(PathElementKind::GenericParameter, type) {
static_assert(alignof(GenericTypeParamType) >= 4,
"archetypes insufficiently aligned");
}
GenericTypeParamType *getType() const {
return getStoredPointer();
}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == PathElementKind::GenericParameter;
}
};
class LocatorPathElt::OpenedGeneric final : public StoredPointerElement<GenericSignatureImpl> {
public:
OpenedGeneric(GenericSignature sig)
: StoredPointerElement(PathElementKind::OpenedGeneric, sig.getPointer()) {}
GenericSignature getSignature() const {
return getStoredPointer();
}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::OpenedGeneric;
}
};
class LocatorPathElt::KeyPathDynamicMember final : public StoredPointerElement<NominalTypeDecl> {
public:
KeyPathDynamicMember(NominalTypeDecl *keyPathDecl)
: StoredPointerElement(PathElementKind::KeyPathDynamicMember, keyPathDecl) {}
NominalTypeDecl *getKeyPathDecl() const {
return getStoredPointer();
}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::KeyPathDynamicMember;
}
};
class LocatorPathElt::TernaryBranch final : public StoredIntegerElement<1> {
public:
TernaryBranch(bool side)
: StoredIntegerElement(ConstraintLocator::TernaryBranch, side) {}
bool forThen() const { return bool(getValue()); }
bool forElse() const { return !bool(getValue()); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::TernaryBranch;
}
};
class LocatorPathElt::PatternMatch final : public StoredPointerElement<Pattern> {
public:
PatternMatch(Pattern *pattern)
: StoredPointerElement(PathElementKind::PatternMatch, pattern) {}
Pattern *getPattern() const { return getStoredPointer(); }
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::PatternMatch;
}
};
class LocatorPathElt::ArgumentAttribute final : public StoredIntegerElement<1> {
public:
enum Attribute : uint8_t { InOut, Escaping };
private:
ArgumentAttribute(Attribute attr)
: StoredIntegerElement(ConstraintLocator::ArgumentAttribute, static_cast<uint8_t>(attr)) {}
public:
Attribute getAttr() const { return static_cast<Attribute>(getValue()); }
static ArgumentAttribute forInOut() {
return ArgumentAttribute(Attribute::InOut);
}
static ArgumentAttribute forEscaping() {
return ArgumentAttribute(Attribute::Escaping);
}
static bool classof(const LocatorPathElt *elt) {
return elt->getKind() == ConstraintLocator::ArgumentAttribute;
}
};
namespace details {
template <typename CustomPathElement>
class PathElement {
static constexpr bool hasStoredValueImpl(...) { return false; }
template <unsigned N>
static constexpr bool hasStoredValueImpl(StoredIntegerElement<N> *) { return true; }
template <typename T>
static constexpr bool hasStoredValueImpl(StoredPointerElement<T> *) { return true; }
public:
static constexpr bool hasStoredValue() {
return hasStoredValueImpl(static_cast<CustomPathElement *>(nullptr));
}
};
}
template <typename CustomPathElement>
constexpr bool isValidCustomPathElement() {
return details::PathElement<CustomPathElement>::hasStoredValue();
}
// All custom path element classes must inherit from StoredIntegerElement or StoredPointerElement
#define CUSTOM_LOCATOR_PATH_ELT(Name) \
static_assert(isValidCustomPathElement<LocatorPathElt::Name>(), \
"Custom path elements must inherit from StoredIntegerElement or StoredPointerElement");
#include "ConstraintLocatorPathElts.def"
/// A simple stack-only builder object that constructs a
/// constraint locator without allocating memory.
///
/// Use this object to build a path when passing components down the
/// stack, e.g., when recursively breaking apart types as in \c matchTypes().
class ConstraintLocatorBuilder {
/// The constraint locator that this builder extends or the
/// previous builder in the chain.
llvm::PointerUnion<ConstraintLocator *, ConstraintLocatorBuilder *>
previous;
/// The current path element, if there is one.
Optional<LocatorPathElt> element;
/// The current set of flags.
unsigned summaryFlags;
ConstraintLocatorBuilder(llvm::PointerUnion<ConstraintLocator *,
ConstraintLocatorBuilder *>
previous,
LocatorPathElt element,
unsigned flags)
: previous(previous), element(element), summaryFlags(flags) { }
public:
ConstraintLocatorBuilder(ConstraintLocator *locator)
: previous(locator), element(),
summaryFlags(locator ? locator->getSummaryFlags() : 0) { }
/// Retrieve a new path with the given path element added to it. Note that
/// the produced locator stores a reference to this locator, and therefore
/// must not outlive it.
ConstraintLocatorBuilder withPathElement(LocatorPathElt newElt) & {
unsigned newFlags = summaryFlags | newElt.getNewSummaryFlags();
if (!element)
return ConstraintLocatorBuilder(previous, newElt, newFlags);
return ConstraintLocatorBuilder(this, newElt, newFlags);
}
/// Determine whether this builder has an empty path.
bool hasEmptyPath() const {
return !element;
}
/// Return the set of flags that summarize this path.
unsigned getSummaryFlags() const {
return summaryFlags;
}
bool isFunctionConversion() const {
return (getSummaryFlags() & ConstraintLocator::IsFunctionConversion);
}
bool isForAutoclosureResult() const {
SmallVector<LocatorPathElt, 4> path;
getLocatorParts(path);
auto last = std::find_if(
path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
return elt.getKind() != ConstraintLocator::OptionalPayload &&
elt.getKind() != ConstraintLocator::GenericArgument;
});
if (last != path.rend())
return last->getKind() == ConstraintLocator::AutoclosureResult;
return false;
}
/// Checks whether this locator is describing an argument application for a
/// non-ephemeral parameter.
bool isNonEphemeralParameterApplication() const {
return (getSummaryFlags() & ConstraintLocator::IsNonEphemeralParam);
}
/// Retrieve the base constraint locator, on which this builder's
/// path is based.
ConstraintLocator *getBaseLocator() const {
for (auto prev = this;
prev;
prev = prev->previous.dyn_cast<ConstraintLocatorBuilder *>()) {
if (auto locator = prev->previous.dyn_cast<ConstraintLocator *>())
return locator;
}
return nullptr;
}
/// Get anchor expression associated with this locator builder.
ASTNode getAnchor() const {
for (auto prev = this; prev;
prev = prev->previous.dyn_cast<ConstraintLocatorBuilder *>()) {
if (auto *locator = prev->previous.dyn_cast<ConstraintLocator *>())
return locator->getAnchor();
}
return {};
}
/// Retrieve the components of the complete locator, which includes
/// the anchor expression and the path.
ASTNode getLocatorParts(SmallVectorImpl<LocatorPathElt> &path) const {
for (auto prev = this;
prev;
prev = prev->previous.dyn_cast<ConstraintLocatorBuilder *>()) {
// If there is an element at this level, add it.
if (prev->element)
path.push_back(*prev->element);
if (auto locator = prev->previous.dyn_cast<ConstraintLocator *>()) {
// We found the end of the chain. Reverse the path we've built up,
// then prepend the locator's path.
std::reverse(path.begin(), path.end());
path.insert(path.begin(),
locator->getPath().begin(),
locator->getPath().end());
return locator->getAnchor();
}
}
// There was no locator. Just reverse the path.
std::reverse(path.begin(), path.end());
return nullptr;
}
/// Attempt to simplify this locator to a single expression.
Expr *trySimplifyToExpr() const;
/// Retrieve the last element in the path, if there is one.
Optional<LocatorPathElt> last() const {
// If we stored a path element here, grab it.
if (element) return *element;
// Otherwise, look in the previous builder if there is one.
if (auto prevBuilder = previous.dyn_cast<ConstraintLocatorBuilder *>())
return prevBuilder->last();
// Next, check the constraint locator itself.
if (auto locator = previous.dyn_cast<ConstraintLocator *>()) {
auto path = locator->getPath();
if (path.empty()) return None;
return path.back();
}
return None;
}
};
} // end namespace constraints
} // end namespace swift
#endif // LLVM_SWIFT_SEMA_CONSTRAINTLOCATOR_H