//===--- 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/LLVM.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 namespace swift { class Expr; 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 { /// The argument of function application. ApplyArgument, /// The function being applied. ApplyFunction, /// Matching an argument to a parameter. ApplyArgToParam, /// A generic parameter being opened. /// /// Also contains the generic parameter type itself. GenericParameter, /// The argument type of a function. FunctionArgument, /// The result type of a function. FunctionResult, /// A tuple element referenced by position. TupleElement, /// A tuple element referenced by name. NamedTupleElement, /// An optional payload. OptionalPayload, /// A generic argument. /// FIXME: Add support for named generic arguments? GenericArgument, /// A member. /// FIXME: Do we need the actual member name here? Member, /// An unresolved member. UnresolvedMember, /// The base of a member expression. MemberRefBase, /// The lookup for a subscript member. SubscriptMember, /// The lookup for a constructor member. ConstructorMember, /// An implicit @lvalue-to-inout conversion; only valid for operator /// arguments. LValueConversion, /// RValue adjustment. RValueAdjustment, /// The result of a closure. ClosureResult, /// The parent of a nested type. ParentType, /// The instance of a metatype type. InstanceType, /// The element type of a sequence in a for ... in ... loop. SequenceElementType, /// An argument passed in an autoclosure parameter /// position, which must match the autoclosure return type. AutoclosureResult, /// The requirement that we're matching during protocol conformance /// checking. Requirement, /// The candidate witness during protocol conformance checking. Witness, /// This is referring to a type produced by opening a generic type at the /// base of the locator. OpenedGeneric, /// A component of a key path. KeyPathComponent, /// The Nth conditional requirement in the parent locator's conformance. ConditionalRequirement, /// A single requirement placed on the type parameters. TypeParameterRequirement, /// Locator for a binding from an IUO disjunction choice. ImplicitlyUnwrappedDisjunctionChoice, /// A result of an expression involving dynamic lookup. DynamicLookupResult, /// The desired contextual type passed in to the constraint system. ContextualType, /// The missing argument synthesized by the solver. SynthesizedArgument, /// The member looked up via keypath based dynamic lookup. KeyPathDynamicMember, /// The type of the key path expression KeyPathType, /// The root of a key path KeyPathRoot, /// The value of a key path KeyPathValue, /// The result type of a key path component. Not used for subscripts. KeyPathComponentResult, /// The expected type of the function with a single expression body. SingleExprFuncResultType, }; /// Determine the number of numeric values used for the given path /// element kind. static unsigned numNumericValuesInPathElement(PathElementKind kind) { switch (kind) { case ApplyArgument: case ApplyFunction: case GenericParameter: case FunctionArgument: case FunctionResult: case OptionalPayload: case Member: case MemberRefBase: case UnresolvedMember: case SubscriptMember: case ConstructorMember: case LValueConversion: case RValueAdjustment: case ClosureResult: case ParentType: case InstanceType: case SequenceElementType: case AutoclosureResult: case Requirement: case Witness: case ImplicitlyUnwrappedDisjunctionChoice: case DynamicLookupResult: case ContextualType: case KeyPathType: case KeyPathRoot: case KeyPathValue: case KeyPathComponentResult: case SingleExprFuncResultType: return 0; case OpenedGeneric: case GenericArgument: case NamedTupleElement: case TupleElement: case KeyPathComponent: case SynthesizedArgument: case KeyPathDynamicMember: return 1; case TypeParameterRequirement: case ConditionalRequirement: case ApplyArgToParam: return 2; } llvm_unreachable("Unhandled PathElementKind in switch."); } /// 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, }; static unsigned getSummaryFlagsForPathElement(PathElementKind kind) { switch (kind) { case ApplyArgument: case ApplyFunction: case ApplyArgToParam: case SequenceElementType: case ClosureResult: case ConstructorMember: case InstanceType: case AutoclosureResult: case OptionalPayload: case Member: case MemberRefBase: case UnresolvedMember: case ParentType: case LValueConversion: case RValueAdjustment: case SubscriptMember: case OpenedGeneric: case GenericParameter: case GenericArgument: case NamedTupleElement: case TupleElement: case Requirement: case Witness: case KeyPathComponent: case ConditionalRequirement: case TypeParameterRequirement: case ImplicitlyUnwrappedDisjunctionChoice: case DynamicLookupResult: case ContextualType: case SynthesizedArgument: case KeyPathDynamicMember: case KeyPathType: case KeyPathRoot: case KeyPathValue: case KeyPathComponentResult: case SingleExprFuncResultType: return 0; case FunctionArgument: case FunctionResult: return IsFunctionConversion; } llvm_unreachable("Unhandled PathElementKind in switch."); } /// 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 { /// Describes the kind of data stored here. enum StoredKind : unsigned char { StoredGenericParameter, StoredRequirement, StoredWitness, StoredGenericSignature, StoredKeyPathDynamicMemberBase, StoredKindAndValue }; /// The actual storage for the path element, which involves both a /// kind and (potentially) a value. /// /// The current storage involves a two-bit "storage kind", which selects /// among the possible value stores. The value stores can either be an /// archetype (for archetype path elements) or an unsigned value that /// stores both the specific kind and the (optional) numeric value of that /// kind. Use \c encodeStorage and \c decodeStorage to work with this value. /// /// \note The "storage kind" is stored in the \c storedKind field. uint64_t storage : 61; /// The kind of value stored in \c storage. Valid values are those /// from the StoredKind enum. uint64_t storedKind : 3; /// Encode a path element kind and a value into the storage format. static uint64_t encodeStorage(PathElementKind kind, unsigned value) { return ((uint64_t)value << 8) | kind; } /// Decode a storage value into path element kind and value. static std::pair decodeStorage(uint64_t storage) { return { (PathElementKind)((unsigned)storage & 0xFF), storage >> 8 }; } PathElement(PathElementKind kind, unsigned value) : storage(encodeStorage(kind, value)), storedKind(StoredKindAndValue) { assert(numNumericValuesInPathElement(kind) == 1 && "Path element kind does not require 1 value"); } PathElement(PathElementKind kind, unsigned value1, unsigned value2) : storage(encodeStorage(kind, value1 << 16 | value2)), storedKind(StoredKindAndValue) { assert(numNumericValuesInPathElement(kind) == 2 && "Path element kind does not require 2 values"); } PathElement(GenericSignature *sig) : storage((reinterpret_cast(sig) >> 3)), storedKind(StoredGenericSignature) {} PathElement(const NominalTypeDecl *keyPath) : storage((reinterpret_cast(keyPath) >> 3)), storedKind(StoredKeyPathDynamicMemberBase) {} friend class ConstraintLocator; public: PathElement(PathElementKind kind) : storage(encodeStorage(kind, 0)), storedKind(StoredKindAndValue) { assert(numNumericValuesInPathElement(kind) == 0 && "Path element requires value"); } PathElement(GenericTypeParamType *type) : storage((reinterpret_cast(type) >> 3)), storedKind(StoredGenericParameter) { static_assert(alignof(GenericTypeParamType) >= 4, "archetypes insufficiently aligned"); assert(getGenericParameter() == type); } PathElement(PathElementKind kind, ValueDecl *decl) : storage((reinterpret_cast(decl) >> 3)), storedKind(kind == Witness ? StoredWitness : StoredRequirement) { assert((kind == Witness || kind == Requirement) && "Not a witness element"); assert(((kind == Requirement && getRequirement() == decl) || (kind == Witness && getWitness() == decl))); } /// Retrieve a path element for a tuple element referred to by /// its position. static PathElement getTupleElement(unsigned position) { return PathElement(TupleElement, position); } /// Retrieve a path element for a tuple element referred to by /// its name. static PathElement getNamedTupleElement(unsigned position) { return PathElement(NamedTupleElement, position); } /// Retrieve a path element for an argument/parameter comparison in a /// function application. static PathElement getApplyArgToParam(unsigned argIdx, unsigned paramIdx) { return PathElement(ApplyArgToParam, argIdx, paramIdx); } /// Retrieve a path element for a generic argument referred to by /// its position. static PathElement getGenericArgument(unsigned position) { return PathElement(GenericArgument, position); } /// Get a path element for a key path component. static PathElement getKeyPathComponent(unsigned position) { return PathElement(KeyPathComponent, position); } static PathElement getOpenedGeneric(GenericSignature *sig) { return PathElement(sig); } /// Get a path element for a conditional requirement. static PathElement getConditionalRequirementComponent(unsigned index, RequirementKind kind) { return PathElement(ConditionalRequirement, index, static_cast(kind)); } static PathElement getTypeRequirementComponent(unsigned index, RequirementKind kind) { return PathElement(TypeParameterRequirement, index, static_cast(kind)); } static PathElement getSynthesizedArgument(unsigned position) { return PathElement(SynthesizedArgument, position); } static PathElement getKeyPathDynamicMember(const NominalTypeDecl *base) { return PathElement(base); } /// Retrieve the kind of path element. PathElementKind getKind() const { switch (static_cast(storedKind)) { case StoredGenericParameter: return GenericParameter; case StoredRequirement: return Requirement; case StoredWitness: return Witness; case StoredGenericSignature: return OpenedGeneric; case StoredKeyPathDynamicMemberBase: return KeyPathDynamicMember; case StoredKindAndValue: return decodeStorage(storage).first; } llvm_unreachable("Unhandled StoredKind in switch."); } /// Retrieve the value associated with this path element, /// if it has one. unsigned getValue() const { unsigned numValues = numNumericValuesInPathElement(getKind()); assert(numValues > 0 && "No value in path element!"); auto value = decodeStorage(storage).second; if (numValues == 1) { return value; } return value >> 16; } /// Retrieve the second value associated with this path element, /// if it has one. unsigned getValue2() const { unsigned numValues = numNumericValuesInPathElement(getKind()); (void)numValues; assert(numValues == 2 && "No second value in path element!"); auto value = decodeStorage(storage).second; return value & 0x00FFFF; } /// Retrieve the declaration for a witness path element. ValueDecl *getWitness() const { assert(getKind() == Witness && "Is not a witness"); return reinterpret_cast(storage << 3); } /// Retrieve the actual archetype for a generic parameter path /// element. GenericTypeParamType *getGenericParameter() const { assert(getKind() == GenericParameter && "Not a generic parameter path element"); return reinterpret_cast(storage << 3); } /// Retrieve the declaration for a requirement path element. ValueDecl *getRequirement() const { assert((static_cast(storedKind) == StoredRequirement) && "Is not a requirement"); return reinterpret_cast(storage << 3); } GenericSignature *getGenericSignature() const { assert((static_cast(storedKind) == StoredGenericSignature) && "Is not an opened generic"); return reinterpret_cast(storage << 3); } NominalTypeDecl *getKeyPath() const { assert((static_cast(storedKind) == StoredKeyPathDynamicMemberBase) && "Is not a keypath dynamic member"); return reinterpret_cast(storage << 3); } /// Return the summary flags for this particular element. unsigned getNewSummaryFlags() const { return getSummaryFlagsForPathElement(getKind()); } bool isTypeParameterRequirement() const { return getKind() == PathElementKind::TypeParameterRequirement; } bool isConditionalRequirement() const { return getKind() == PathElementKind::ConditionalRequirement; } bool isSynthesizedArgument() const { return getKind() == PathElementKind::SynthesizedArgument; } bool isKeyPathDynamicMember() const { return getKind() == PathElementKind::KeyPathDynamicMember; } bool isKeyPathComponent() const { return getKind() == PathElementKind::KeyPathComponent; } }; /// Return the summary flags for an entire path. static unsigned getSummaryFlagsForPath(ArrayRef path) { unsigned flags = 0; for (auto &elt : path) flags |= elt.getNewSummaryFlags(); return flags; } /// Retrieve the expression that anchors this locator. Expr *getAnchor() const { return anchor; } /// Retrieve the path that extends from the anchor to a specific /// subcomponent. ArrayRef getPath() const { // FIXME: Alignment. return llvm::makeArrayRef(reinterpret_cast(this + 1), numPathElements); } unsigned getSummaryFlags() const { return summaryFlags; } /// Determines whether this locator is part of a function /// conversion. bool isFunctionConversion() const { return (getSummaryFlags() & IsFunctionConversion); } /// 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; /// Produce a profile of this locator, for use in a folding set. static void Profile(llvm::FoldingSetNodeID &id, Expr *anchor, ArrayRef 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. LLVM_ATTRIBUTE_DEPRECATED( void dump(SourceManager *SM) LLVM_ATTRIBUTE_USED, "only for use within the debugger"); LLVM_ATTRIBUTE_DEPRECATED( void dump(ConstraintSystem *CS) LLVM_ATTRIBUTE_USED, "only for use within the debugger"); void dump(SourceManager *SM, raw_ostream &OS) LLVM_ATTRIBUTE_USED; private: /// Initialize a constraint locator with an anchor and a path. ConstraintLocator(Expr *anchor, ArrayRef path, unsigned flags) : anchor(anchor), numPathElements(path.size()), summaryFlags(flags) { // FIXME: Alignment. std::copy(path.begin(), path.end(), reinterpret_cast(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, Expr *anchor, ArrayRef 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. Expr *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; /// 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 previous; /// The current path element, if there is one. Optional element; /// The current set of flags. unsigned summaryFlags; ConstraintLocatorBuilder(llvm::PointerUnion 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. 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); } /// 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()) { if (auto locator = prev->previous.dyn_cast()) return locator; } return nullptr; } /// Get anchor expression associated with this locator builder. Expr *getAnchor() const { for (auto prev = this; prev; prev = prev->previous.dyn_cast()) { if (auto *locator = prev->previous.dyn_cast()) return locator->getAnchor(); } return nullptr; } /// Retrieve the components of the complete locator, which includes /// the anchor expression and the path. Expr *getLocatorParts(SmallVectorImpl &path) const { for (auto prev = this; prev; prev = prev->previous.dyn_cast()) { // 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()) { // 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 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()) return prevBuilder->last(); // Next, check the constraint locator itself. if (auto locator = previous.dyn_cast()) { 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