//===--- ConstraintGraph.h - Constraint Graph -------------------*- 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 \c PotentialBindings class and its auxiliary types // such as \c PotentialBinding, that are used to describe bindings which // a particular type variable could be bound to. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SEMA_CSBINDINGS_H #define SWIFT_SEMA_CSBINDINGS_H #include "swift/AST/ASTNode.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/LLVMExtras.h" #include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintLocator.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/raw_ostream.h" #include namespace swift { class DeclContext; enum class KnownProtocolKind : uint8_t; class ProtocolDecl; namespace constraints { class ConstraintSystem; namespace inference { /// The kind of bindings that are permitted. enum class AllowedBindingKind : uint8_t { /// Only the exact type. Exact, /// Supertypes of the specified type. Supertypes, /// Subtypes of the specified type. Subtypes }; /// The kind of literal binding found. enum class LiteralBindingKind : uint8_t { None, Collection, Float, Atom, }; /// A potential binding from the type variable to a particular type, /// along with information that can be used to construct related /// bindings, e.g., the supertypes of a given type. struct PotentialBinding { /// The type to which the type variable can be bound. Type BindingType; /// The kind of bindings permitted. AllowedBindingKind Kind; /// The source of the type information. /// /// Determines whether this binding represents a "hole" in /// constraint system. Such bindings have no originating constraint /// because they are synthetic, they have a locator instead. PointerUnion BindingSource; PotentialBinding(Type type, AllowedBindingKind kind, PointerUnion source) : BindingType(type), Kind(kind), BindingSource(source) {} PotentialBinding(Type type, AllowedBindingKind kind, Constraint *source) : PotentialBinding( type, kind, PointerUnion(source)) {} bool isDefaultableBinding() const { if (auto *constraint = BindingSource.dyn_cast()) return constraint->getKind() == ConstraintKind::Defaultable; // If binding source is not constraint - it's a hole, which is // a last resort default binding for a type variable. return true; } bool hasDefaultedLiteralProtocol() const { return bool(getDefaultedLiteralProtocol()); } ProtocolDecl *getDefaultedLiteralProtocol() const { auto *constraint = BindingSource.dyn_cast(); if (!constraint) return nullptr; return constraint->getKind() == ConstraintKind::LiteralConformsTo ? constraint->getProtocol() : nullptr; } ConstraintLocator *getLocator() const { if (auto *constraint = BindingSource.dyn_cast()) return constraint->getLocator(); return cast(BindingSource); } Constraint *getSource() const { return cast(BindingSource); } PotentialBinding withType(Type type) const { return {type, Kind, BindingSource}; } PotentialBinding withSameSource(Type type, AllowedBindingKind kind) const { return {type, kind, BindingSource}; } /// Determine whether this binding could be a viable candidate /// to be "joined" with some other binding. It has to be at least /// a non-default r-value supertype binding with no type variables. bool isViableForJoin() const; static PotentialBinding forHole(TypeVariableType *typeVar, ConstraintLocator *locator) { return {PlaceholderType::get(typeVar->getASTContext(), typeVar), AllowedBindingKind::Exact, /*source=*/locator}; } static PotentialBinding forPlaceholder(Type placeholderTy) { return {placeholderTy, AllowedBindingKind::Exact, PointerUnion()}; } void print(llvm::raw_ostream &out, const PrintOptions &PO) const; }; struct LiteralRequirement { /// The source of the literal requirement. Constraint *Source; /// The default type associated with this literal (if any). Type DefaultType; /// Determines whether this literal is a direct requirement /// of the current type variable. bool IsDirectRequirement; /// If the literal is covered by existing type binding, /// this points to the source of the binding. mutable Constraint *CoveredBy = nullptr; LiteralRequirement(Constraint *source, Type defaultTy, bool isDirect) : Source(source), DefaultType(defaultTy), IsDirectRequirement(isDirect) {} Constraint *getSource() const { return Source; } ProtocolDecl *getProtocol() const { return Source->getProtocol(); } bool isCovered() const { return bool(CoveredBy); } bool isDirectRequirement() const { return IsDirectRequirement; } bool hasDefaultType() const { return bool(DefaultType); } Type getDefaultType() const { assert(hasDefaultType()); return DefaultType; } void setCoveredBy(Constraint *coveredBy) { assert(!isCovered()); CoveredBy = coveredBy; } /// Determines whether this literal requirement is "covered" /// by the given binding - type of the binding could either be /// equal (in canonical sense) to the protocol's default type, /// or conform to a protocol. /// /// \param binding The binding to check for coverage. /// /// \param canBeNil The flag that determines whether given type /// variable requires all of its bindings to be optional. /// /// \param CS The constraint system this literal requirement belongs to. /// /// \returns a pair of bool and a type: /// - bool, true if binding covers given literal protocol; /// - type, non-null if binding type has to be adjusted /// to cover given literal protocol; std::pair isCoveredBy(const PotentialBinding &binding, bool canBeNil, ConstraintSystem &CS) const; /// Determines whether literal protocol associated with this /// meta-information is viable for inclusion as a defaultable binding. bool viableAsBinding() const { return !isCovered() && hasDefaultType(); } private: bool isCoveredBy(Type type, ConstraintSystem &CS) const; }; struct PotentialBindings { /// The set of all constraints that have been added via infer(). llvm::SmallSetVector Constraints; /// The set of potential bindings. llvm::SmallVector Bindings; /// The set of constraints which delay attempting this type variable. llvm::TinyPtrVector DelayedBy; /// The set of type variables adjacent to the current one. /// /// Type variables contained here are either related through the /// bindings (contained in the binding type e.g. `Foo<$T0>`), or /// reachable through subtype/conversion relationship e.g. /// `$T0 subtype of $T1` or `$T0 arg conversion $T1`. llvm::SmallVector, 2> AdjacentVars; /// A set of all not-yet-resolved type variables this type variable /// is a subtype of, supertype of or is equivalent to. This is used /// to determine ordering inside of a chain of subtypes to help infer /// transitive bindings and protocol requirements. llvm::SmallVector, 4> SubtypeOf; llvm::SmallVector, 4> SupertypeOf; llvm::SmallVector, 4> EquivalentTo; ASTNode AssociatedCodeCompletionToken = ASTNode(); /// Add a potential binding to the list of bindings, /// coalescing supertype bounds when we are able to compute the meet. void addPotentialBinding(TypeVariableType *typeVar, PotentialBinding binding); bool isSubtypeOf(TypeVariableType *typeVar) const { return llvm::any_of( SubtypeOf, [&typeVar](const std::pair &subtype) { return subtype.first == typeVar && subtype.second->getKind() == ConstraintKind::Subtype; }); } private: /// Attempt to infer a new binding and other useful information /// (i.e. whether bindings should be delayed) from the given /// relational constraint. std::optional inferFromRelational( ConstraintSystem &CS, TypeVariableType *TypeVar, Constraint *constraint); public: void infer(ConstraintSystem &CS, TypeVariableType *TypeVar, Constraint *constraint); /// Retract all bindings and other information related to a given /// constraint from this binding set. /// /// This would happen when constraint is simplified or solver backtracks /// (either from overload choice or (some) type variable binding). void retract(ConstraintSystem &CS, TypeVariableType *TypeVar, Constraint *constraint); void reset(); void dump(ConstraintSystem &CS, TypeVariableType *TypeVar, llvm::raw_ostream &out, unsigned indent) const; }; } // end namespace inference } // end namespace constraints } // end namespace swift namespace llvm { template <> struct DenseMapInfo { using Binding = swift::constraints::inference::PotentialBinding; static Binding getEmptyKey() { return placeholderKey(llvm::DenseMapInfo::getEmptyKey()); } static Binding getTombstoneKey() { return placeholderKey( llvm::DenseMapInfo::getTombstoneKey()); } static unsigned getHashValue(const Binding &Val) { return DenseMapInfo::getHashValue( Val.BindingType->getCanonicalType()); } static bool isEqual(const Binding &LHS, const Binding &RHS) { auto lhsTy = LHS.BindingType.getPointer(); auto rhsTy = RHS.BindingType.getPointer(); // Fast path: pointer equality. if (DenseMapInfo::isEqual(lhsTy, rhsTy)) return true; // If either side is empty or tombstone, let's use pointer equality. { auto emptyTy = llvm::DenseMapInfo::getEmptyKey(); auto tombstoneTy = llvm::DenseMapInfo::getTombstoneKey(); if (lhsTy == emptyTy || lhsTy == tombstoneTy) return lhsTy == rhsTy; if (rhsTy == emptyTy || rhsTy == tombstoneTy) return lhsTy == rhsTy; } // Otherwise let's drop the sugar and check. return LHS.BindingType->isEqual(RHS.BindingType); } private: static Binding placeholderKey(swift::Type type) { return Binding::forPlaceholder(type); } }; } // end namespace llvm namespace swift { namespace constraints { namespace inference { class BindingSet { using BindingScore = std::tuple; ConstraintSystem &CS; TypeVariableType *TypeVar; const PotentialBindings &Info; llvm::SmallPtrSet AdjacentVars; public: swift::SmallSetVector Bindings; /// The set of protocol conformance requirements placed on this type variable. llvm::SmallVector Protocols; /// The set of unique literal protocol requirements placed on this /// type variable or inferred transitively through subtype chains. /// /// Note that ordering is important when it comes to bindings, we'd /// like to add any "direct" default types first to attempt them /// before transitive ones. llvm::SmallMapVector Literals; llvm::SmallDenseMap Defaults; /// The set of transitive protocol requirements inferred through /// subtype/conversion/equivalence relations with other type variables. std::optional> TransitiveProtocols; BindingSet(ConstraintSystem &CS, TypeVariableType *TypeVar, const PotentialBindings &info); BindingSet(BindingSet &&other) = default; BindingSet(const BindingSet &other) = delete; ConstraintSystem &getConstraintSystem() const { return CS; } TypeVariableType *getTypeVariable() const { return TypeVar; } /// Check whether this binding set belongs to a type variable /// that represents a result type of a closure. bool forClosureResult() const; /// Check whether this binding set belongs to a type variable /// that represents a generic parameter. bool forGenericParameter() const; bool canBeNil() const; /// If this type variable doesn't have any viable bindings, or /// if there is only one binding and it's a placeholder type, consider /// this type variable to be a hole in a constraint system /// regardless of where the placeholder type originated. bool isHole() const { if (isDirectHole()) return true; if (Bindings.size() != 1) return false; const auto &binding = Bindings.front(); return binding.BindingType->is(); } /// Determines whether the only possible binding for this type variable /// would be a placeholder type. This is different from `isHole` method /// because type variable could also acquire a placeholder type transitively /// if one of the type variables in its subtype/equivalence chain has been /// bound to a placeholder type. bool isDirectHole() const; /// Determine whether attempting this type variable should be /// delayed until the rest of the constraint system is considered /// "fully bound" meaning constraints, which affect completeness /// of the binding set, for this type variable such as - member /// constraint, disjunction, function application etc. - are simplified. /// /// Note that in some situations i.e. when there are no more /// disjunctions or type variables left to attempt, it's still /// okay to attempt "delayed" type variable to make forward progress. bool isDelayed() const; /// Whether the bindings of this type involve other type variables, /// or the type variable itself is adjacent to other type variables /// that could become valid bindings in the future. bool involvesTypeVariables() const; /// Whether the bindings represent (potentially) incomplete set, /// there is no way to say with absolute certainty if that's the /// case, but that could happen when certain constraints like /// `bind param` are present in the system. bool isPotentiallyIncomplete() const; /// Determine if the bindings only constrain the type variable from above /// with an existential type; such a binding is not very helpful because /// it's impossible to enumerate the existential type's subtypes. bool isSubtypeOfExistentialType() const { if (Bindings.empty()) return false; // Literal requirements always result in a subtype/supertype // relationship to a concrete type. if (llvm::any_of(Literals, [](const auto &literal) { return literal.second.viableAsBinding(); })) return false; return llvm::all_of(Bindings, [](const PotentialBinding &binding) { return binding.BindingType->isExistentialType() && binding.Kind == AllowedBindingKind::Subtypes; }); } /// Check if this binding is viable for inclusion in the set. /// /// \param binding The binding to validate. /// \param isTransitive Indicates whether this binding has been /// acquired through transitive inference and requires extra /// checking. bool isViable(PotentialBinding &binding, bool isTransitive); /// Determine whether this set has any "viable" (or non-hole) bindings. /// /// A viable binding could be - a direct or transitive binding /// inferred from a constraint, literal binding, or defaultable /// binding. /// /// A hole is not considered a viable binding since it doesn't /// add any new type information to constraint system. bool hasViableBindings() const { return !Bindings.empty() || getNumViableLiteralBindings() > 0 || !Defaults.empty(); } /// Determine whether this set can be chosen as the next binding set /// to attempt. bool isViable() const { return hasViableBindings() || isDirectHole(); } ArrayRef getConformanceRequirements() const { return Protocols; } unsigned getNumViableLiteralBindings() const; unsigned getNumViableDefaultableBindings() const { if (isDirectHole()) return 1; auto numDefaultable = llvm::count_if( Defaults, [](const std::pair &entry) { return entry.second->getKind() == ConstraintKind::Defaultable; }); // Short-circuit unviable checks if there are no defaultable bindings. if (numDefaultable == 0) return 0; // Defaultable constraint is unviable if its type is covered by // an existing direct or transitive binding. auto unviable = llvm::count_if(Bindings, [&](const PotentialBinding &binding) { auto type = binding.BindingType->getCanonicalType(); auto def = Defaults.find(type); return def != Defaults.end() ? def->second->getKind() == ConstraintKind::Defaultable : false; }); assert(numDefaultable >= unviable); return numDefaultable - unviable; } ASTNode getAssociatedCodeCompletionToken() const { return Info.AssociatedCodeCompletionToken; } void forEachLiteralRequirement( llvm::function_ref callback) const; void forEachAdjacentVariable( llvm::function_ref callback) const { for (auto *typeVar : AdjacentVars) callback(typeVar); } /// Return a literal requirement that has the most impact on the binding /// score. LiteralBindingKind getLiteralForScore() const; /// Check if this binding is favored over a disjunction e.g. /// if it has only concrete types or would resolve a closure. bool favoredOverDisjunction(Constraint *disjunction) const; /// Check if this binding is favored over a conjunction. bool favoredOverConjunction(Constraint *conjunction) const; void inferTransitiveKeyPathBindings(); /// Detect `subtype` relationship between two type variables and /// attempt to infer supertype bindings transitively e.g. /// /// Given A <: T1 <: T2 transitively A <: T2 /// /// Which gives us a new (superclass A) binding for T2 as well as T1. /// /// \param inferredBindings The set of all bindings inferred for type /// variables in the workset. void inferTransitiveSupertypeBindings(); void inferTransitiveUnresolvedMemberRefBindings(); /// Detect subtype, conversion or equivalence relationship /// between two type variables and attempt to propagate protocol /// requirements down the subtype or equivalence chain. void inferTransitiveProtocolRequirements(); /// Check whether the given binding set covers any of the literal protocols /// associated with this type variable. The idea is that if a type variable /// has a binding like Int and also it has a conformance requirement to /// ExpressibleByIntegerLitral, we can avoid attempting the default type of /// that literal literal if we already attempted Int. void determineLiteralCoverage(); /// Finalize binding computation for key path type variables. /// /// \returns true if finalization successful (which makes binding set viable), /// and false otherwise. bool finalizeKeyPathBindings(); /// Handle diagnostics of unresolved member chains. void finalizeUnresolvedMemberChainResult(); static BindingScore formBindingScore(const BindingSet &b); bool operator==(const BindingSet &other); /// Compare two sets of bindings, where \c this < other indicates that /// \c this is a better set of bindings that \c other. bool operator<(const BindingSet &other); void dump(llvm::raw_ostream &out, unsigned indent) const; private: /// Add a new binding to the set. /// /// \param binding The binding to add. /// \param isTransitive Indicates whether this binding has been /// acquired through transitive inference and requires validity /// checking. void addBinding(PotentialBinding binding, bool isTransitive); void addLiteralRequirement(Constraint *literal); void addDefault(Constraint *constraint); StringRef getLiteralBindingKind(LiteralBindingKind K) const { #define ENTRY(Kind, String) \ case LiteralBindingKind::Kind: \ return String switch (K) { ENTRY(None, "none"); ENTRY(Collection, "collection"); ENTRY(Float, "float"); ENTRY(Atom, "atom"); } #undef ENTRY } }; } // namespace inference } // namespace constraints } // namespace swift #endif // SWIFT_SEMA_CSBINDINGS_H