//===--- ConstraintSystem.h - Constraint-based Type Checking ----*- 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 constraint-based type checker, anchored by the // \c ConstraintSystem class, which provides type checking and type // inference for expressions. // //===----------------------------------------------------------------------===// #ifndef SWIFT_SEMA_CONSTRAINT_SYSTEM_H #define SWIFT_SEMA_CONSTRAINT_SYSTEM_H #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/AnyFunctionRef.h" #include "swift/AST/NameLookup.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/Types.h" #include "swift/Basic/Debug.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/OptionSet.h" #include "swift/Sema/CSFix.h" #include "swift/Sema/CSTrail.h" #include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintGraph.h" #include "swift/Sema/ConstraintLocator.h" #include "swift/Sema/OverloadChoice.h" #include "swift/Sema/Solution.h" #include "swift/Sema/SolutionResult.h" #include "swift/Sema/SyntacticElementTarget.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetOperations.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/ilist.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include #include namespace swift { class Expr; class FuncDecl; class BraseStmt; enum class TypeCheckExprFlags; namespace constraints { class ConstraintSystem; class SyntacticElementTarget; // PreparedOverload.h struct DeclReferenceType; class PreparedOverload; struct PreparedOverloadBuilder; // Subtyping.h enum class ConversionBehavior : unsigned; // CSDisjunction.h class SolverDisjunction; } // end namespace constraints // Forward declare some TypeChecker related functions // so they could be made friends of ConstraintSystem. namespace TypeChecker { std::optional applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); std::optional typeCheckExpression(constraints::SyntacticElementTarget &target, OptionSet options, DiagnosticTransaction *diagnosticTransaction = nullptr); std::optional typeCheckTarget(constraints::SyntacticElementTarget &target, OptionSet options, DiagnosticTransaction *diagnosticTransaction = nullptr); Type typeCheckParameterDefault(Expr *&, DeclContext *, Type, bool, bool); } // end namespace TypeChecker } // end namespace swift namespace swift { /// Specify how we handle the binding of underconstrained (free) type variables /// within a solution to a constraint system. enum class FreeTypeVariableBinding { /// Disallow any binding of such free type variables. Disallow, /// Allow the free type variables to persist in the solution. Allow }; /// Describes whether or not a result builder method is supported. struct ResultBuilderOpSupport { enum Classification { Unsupported, Unavailable, Supported }; Classification Kind; ResultBuilderOpSupport(Classification Kind) : Kind(Kind) {} /// Returns whether or not the builder method is supported. If /// \p requireAvailable is true, an unavailable method will be considered /// unsupported. bool isSupported(bool requireAvailable) const { switch (Kind) { case Unsupported: return false; case Unavailable: return !requireAvailable; case Supported: return true; } llvm_unreachable("Unhandled case in switch!"); } }; namespace constraints { struct ResultBuilder { private: DeclContext *DC; /// An implicit variable that represents `Self` type of the result builder. VarDecl *BuilderSelf; Type BuilderType; /// Cache of supported result builder operations. llvm::SmallDenseMap SupportedOps; Identifier BuildOptionalId; /// Counter used to give unique names to the variables that are /// created implicitly. unsigned VarCounter = 0; public: ResultBuilder(ConstraintSystem &CS, DeclContext *DC, Type builderType); DeclContext *getDeclContext() const { return DC; } Type getType() const { return BuilderType; } NominalTypeDecl *getBuilderDecl() const { return BuilderType->getAnyNominal(); } VarDecl *getBuilderSelf() const { return BuilderSelf; } Identifier getBuildOptionalId() const { return BuildOptionalId; } bool supports(Identifier fnBaseName, ArrayRef argLabels = {}, bool checkAvailability = false); bool supportsOptional() { return supports(getBuildOptionalId()); } /// Checks whether the `buildPartialBlock` method is supported. bool supportsBuildPartialBlock(bool checkAvailability); /// Checks whether the builder can use `buildPartialBlock` to combine /// expressions, instead of `buildBlock`. bool canUseBuildPartialBlock(); Expr *buildCall(SourceLoc loc, Identifier fnName, ArrayRef argExprs, ArrayRef argLabels) const; /// Build an implicit variable in this context. VarDecl *buildVar(SourceLoc loc); /// Build a reference to a given variable and mark it /// as located at a given source location. DeclRefExpr *buildVarRef(VarDecl *var, SourceLoc loc); }; class ConstraintLocator; /// Describes a conversion restriction or a fix. struct RestrictionOrFix { union { ConversionRestrictionKind Restriction; ConstraintFix *TheFix; }; bool IsRestriction; public: RestrictionOrFix(ConversionRestrictionKind restriction) : Restriction(restriction), IsRestriction(true) { } RestrictionOrFix(ConstraintFix *fix) : TheFix(fix), IsRestriction(false) {} std::optional getRestriction() const { if (IsRestriction) return Restriction; return std::nullopt; } std::optional getFix() const { if (!IsRestriction) return TheFix; return std::nullopt; } }; class ComplexityTracker { ConstraintSystem &CS; llvm::TimeRecord StartTime; /// The number of seconds from creation until /// this tracker is considered expired. unsigned ThresholdInSecs; /// Threshold (in milliseconds) for emitting a wall-clock-based warning. /// 0 disables the warning. unsigned WarnTimeLimitInMillis; /// Threshold for emitting a solver-scope-based warning. /// 0 disables the warning. unsigned WarnScopeLimit; /// Threshold for emitting a solver-trail-step-based warning. /// 0 disables the warning. unsigned WarnTrailLimit; bool PrintWarning; public: const static unsigned NoLimit = (unsigned) -1; ComplexityTracker(ConstraintSystem &CS, unsigned thresholdInSecs, unsigned warnTimeLimitInMillis, unsigned warnScopeLimit, unsigned warnTrailLimit); ~ComplexityTracker(); llvm::TimeRecord startedAt() const { return StartTime; } /// Return the elapsed process time (including fractional seconds) /// as a double. double getElapsedProcessTimeInFractionalSeconds() const { llvm::TimeRecord endTime = llvm::TimeRecord::getCurrentTime(false); return endTime.getProcessTime() - StartTime.getProcessTime(); } /// Return the remaining process time in seconds until the /// threshold specified during construction is reached. unsigned getRemainingProcessTimeInSeconds() const { if (ThresholdInSecs == NoLimit) return NoLimit; auto elapsed = unsigned(getElapsedProcessTimeInFractionalSeconds()); return elapsed >= ThresholdInSecs ? 0 : ThresholdInSecs - elapsed; } // Disable emission of warnings about expressions that take longer // than the warning threshold. void disableWarning() { PrintWarning = false; } bool isExpired() const { return getRemainingProcessTimeInSeconds() == 0; } }; } // end namespace constraints enum class KeyPathMutability : uint8_t { ReadOnly, Writable, ReferenceWritable }; using KeyPathCapability = std::pair; namespace constraints { template T *castToExpr(ASTNode node) { return cast(cast(node)); } template T *getAsExpr(ASTNode node) { if (node.isNull()) return nullptr; if (auto *E = node.dyn_cast()) return dyn_cast_or_null(E); return nullptr; } template bool isExpr(ASTNode node) { if (node.isNull() || !isa(node)) return false; auto *E = cast(node); return isa(E); } template T *getAsDecl(ASTNode node) { if (auto *E = node.dyn_cast()) return dyn_cast_or_null(E); return nullptr; } template T *getAsTypeRepr(ASTNode node) { if (auto *type = node.dyn_cast()) return dyn_cast_or_null(type); return nullptr; } template T *getAsStmt(ASTNode node) { if (auto *S = node.dyn_cast()) return dyn_cast_or_null(S); return nullptr; } template T *getAsPattern(ASTNode node) { if (auto *P = node.dyn_cast()) return dyn_cast_or_null(P); return nullptr; } template T *castToStmt(ASTNode node) { return cast(cast(node)); } SourceLoc getLoc(ASTNode node); SourceRange getSourceRange(ASTNode node); /// The result of comparing two constraint systems that are a solutions /// to the given set of constraints. enum class SolutionCompareResult { /// The two solutions are incomparable, because, e.g., because one /// solution has some better decisions and some worse decisions than the /// other. Incomparable, /// The two solutions are identical. Identical, /// The first solution is better than the second. Better, /// The second solution is better than the first. Worse }; /// Key to the constraint solver's mapping from AST nodes to their corresponding /// target. struct SyntacticElementTargetKey { enum class Kind { empty, tombstone, stmtCondElement, expr, closure, stmt, pattern, patternBindingEntry, varDecl, functionRef, }; Kind kind; union { StmtConditionElement *stmtCondElement; Expr *expr; Stmt *stmt; Pattern *pattern; struct PatternBindingEntry { PatternBindingDecl *patternBinding; unsigned index; } patternBindingEntry; VarDecl *varDecl; DeclContext *functionRef; } storage; SyntacticElementTargetKey(Kind kind) { assert(kind == Kind::empty || kind == Kind::tombstone); this->kind = kind; } SyntacticElementTargetKey(StmtConditionElement *stmtCondElement) { kind = Kind::stmtCondElement; storage.stmtCondElement = stmtCondElement; } SyntacticElementTargetKey(Expr *expr) { kind = Kind::expr; storage.expr = expr; } SyntacticElementTargetKey(ClosureExpr *closure) { kind = Kind::closure; storage.expr = closure; } SyntacticElementTargetKey(Stmt *stmt) { kind = Kind::stmt; storage.stmt = stmt; } SyntacticElementTargetKey(Pattern *pattern) { kind = Kind::pattern; storage.pattern = pattern; } SyntacticElementTargetKey(PatternBindingDecl *patternBinding, unsigned index) { kind = Kind::patternBindingEntry; storage.patternBindingEntry.patternBinding = patternBinding; storage.patternBindingEntry.index = index; } SyntacticElementTargetKey(VarDecl *varDecl) { kind = Kind::varDecl; storage.varDecl = varDecl; } SyntacticElementTargetKey(DeclContext *dc) { kind = Kind::functionRef; storage.functionRef = dc; } SyntacticElementTargetKey(AnyFunctionRef functionRef) { kind = Kind::functionRef; storage.functionRef = functionRef.getAsDeclContext(); } friend bool operator==(SyntacticElementTargetKey lhs, SyntacticElementTargetKey rhs) { if (lhs.kind != rhs.kind) return false; switch (lhs.kind) { case Kind::empty: case Kind::tombstone: return true; case Kind::stmtCondElement: return lhs.storage.stmtCondElement == rhs.storage.stmtCondElement; case Kind::expr: case Kind::closure: return lhs.storage.expr == rhs.storage.expr; case Kind::stmt: return lhs.storage.stmt == rhs.storage.stmt; case Kind::pattern: return lhs.storage.pattern == rhs.storage.pattern; case Kind::patternBindingEntry: return (lhs.storage.patternBindingEntry.patternBinding == rhs.storage.patternBindingEntry.patternBinding) && (lhs.storage.patternBindingEntry.index == rhs.storage.patternBindingEntry.index); case Kind::varDecl: return lhs.storage.varDecl == rhs.storage.varDecl; case Kind::functionRef: return lhs.storage.functionRef == rhs.storage.functionRef; } llvm_unreachable("invalid SyntacticElementTargetKey kind"); } friend bool operator!=(SyntacticElementTargetKey lhs, SyntacticElementTargetKey rhs) { return !(lhs == rhs); } unsigned getHashValue() const { using llvm::hash_combine; using llvm::DenseMapInfo; switch (kind) { case Kind::empty: case Kind::tombstone: return llvm::DenseMapInfo::getHashValue(static_cast(kind)); case Kind::stmtCondElement: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.stmtCondElement)); case Kind::expr: case Kind::closure: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.expr)); case Kind::stmt: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.stmt)); case Kind::pattern: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.pattern)); case Kind::patternBindingEntry: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue( storage.patternBindingEntry.patternBinding), DenseMapInfo::getHashValue( storage.patternBindingEntry.index)); case Kind::varDecl: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.varDecl)); case Kind::functionRef: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.functionRef)); } llvm_unreachable("invalid statement kind"); } SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS) const LLVM_ATTRIBUTE_USED; }; /// Describes the differences between several solutions to the same /// constraint system. class SolutionDiff { public: /// A difference between two overloads. struct OverloadDiff { /// The locator that describes where the overload comes from. ConstraintLocator *locator; /// The choices that each solution made. SmallVector choices; }; /// The differences between the overload choices between the /// solutions. SmallVector overloads; /// Compute the differences between the given set of solutions. /// /// \param solutions The set of solutions. explicit SolutionDiff(ArrayRef solutions); }; /// An intrusive, doubly-linked list of constraints. using ConstraintList = llvm::ilist; enum class ConstraintSystemFlags { /// Whether we allow the solver to attempt fixes to the system. AllowFixes = 0x01, /// Set if the client wants diagnostics suppressed. SuppressDiagnostics = 0x02, /// If set, verbose output is enabled for this constraint system. /// /// Note that this flag is automatically applied to all constraint systems, /// when \c DebugConstraintSolver is set in \c TypeCheckerOptions. It can be /// automatically enabled for select constraint solving attempts by setting /// \c DebugConstraintSolverAttempt. Finally, it can also be automatically /// enabled for a pre-configured set of expressions on line numbers by setting /// \c DebugConstraintSolverOnLines. DebugConstraints = 0x04, /// If set, we are solving specifically to determine the type of a /// CodeCompletionExpr, and should continue in the presence of errors wherever /// possible. ForCodeCompletion = 0x08, /// Include Clang function types when checking equality for function types. /// /// When LangOptions.UseClangFunctionTypes is set, we can synthesize /// different @convention(c) function types with the same parameter and result /// types (similarly for blocks). This difference is reflected in the `cType:` /// field (@convention(c, cType: ...)). When the cType is different, the types /// should be treated as semantically different, as they may have different /// calling conventions, say due to Clang attributes such as /// `__attribute__((ns_consumed))`. UseClangFunctionTypes = 0x10, /// When set, ignore async/sync mismatches IgnoreAsyncSyncMismatch = 0x20, /// Disable macro expansions. DisableMacroExpansions = 0x40, }; /// Options that affect the constraint system as a whole. using ConstraintSystemOptions = OptionSet; /// This struct represents the results of a member lookup of struct MemberLookupResult { enum { /// This result indicates that we cannot begin to solve this, because the /// base expression is a type variable. Unsolved, /// This result indicates that the member reference is erroneous, but was /// already diagnosed. Don't emit another error. ErrorAlreadyDiagnosed, /// This result indicates that the lookup produced candidate lists, /// potentially of viable results, potentially of error candidates, and /// potentially empty lists, indicating that there were no matches. HasResults } OverallResult; /// This is a list of viable candidates that were matched. /// SmallVector ViableCandidates; /// The number of optional unwraps that were applied implicitly in the /// lookup, for contexts where that is permitted. unsigned numImplicitOptionalUnwraps = 0; /// The base lookup type used to find the results, which will be non-null /// only when it differs from the provided base type. Type actualBaseType; /// This enum tracks reasons why a candidate is not viable. enum UnviableReason { /// This uses a type like Self in its signature that cannot be used on an /// existential box. UR_UnavailableInExistential, /// This is an instance member being accessed through something of metatype /// type. UR_InstanceMemberOnType, /// This is a static/class member being accessed through an instance. UR_TypeMemberOnInstance, /// This is a mutating member, being used on an rvalue. UR_MutatingMemberOnRValue, /// The getter for this subscript or computed property is mutating and we /// only have an rvalue base. This is more specific than the former one. UR_MutatingGetterOnRValue, /// The member is inaccessible (e.g. a private member in another file). UR_Inaccessible, /// This is a `WritableKeyPath` being used to look up read-only member, /// used in situations involving dynamic member lookup via keypath, /// because it's not known upfront what access capability would the /// member have. UR_WritableKeyPathOnReadOnlyMember, /// This is a `ReferenceWritableKeyPath` being used to look up mutating /// member, used in situations involving dynamic member lookup via keypath, /// because it's not known upfront what access capability would the /// member have. UR_ReferenceWritableKeyPathOnMutatingMember, /// This is a KeyPath whose root type is AnyObject UR_KeyPathWithAnyObjectRootType, /// This is a static member being access through a protocol metatype /// but its result type doesn't conform to this protocol. UR_InvalidStaticMemberOnProtocolMetatype, /// This is a member that doesn't appear in 'initializes' and/or /// 'accesses' attributes of the init accessor and therefore canno /// t be referenced in its body. UR_UnavailableWithinInitAccessor, /// The module selector in the `DeclNameRef` does not match this candidate. UR_WrongModule }; /// This is a list of considered (but rejected) candidates, along with a /// reason for their rejection. Split into separate collections to make /// it easier to use in conjunction with viable candidates. SmallVector UnviableCandidates; SmallVector UnviableReasons; /// Mark this as being an already-diagnosed error and return itself. MemberLookupResult &markErrorAlreadyDiagnosed() { OverallResult = ErrorAlreadyDiagnosed; return *this; } void addViable(OverloadChoice candidate) { ViableCandidates.push_back(candidate); } void addUnviable(OverloadChoice candidate, UnviableReason reason) { UnviableCandidates.push_back(candidate); UnviableReasons.push_back(reason); } }; /// Stores the required methods for @dynamicCallable types. struct DynamicCallableMethods { llvm::DenseSet argumentsMethods; llvm::DenseSet keywordArgumentsMethods; void addArgumentsMethod(FuncDecl *method) { argumentsMethods.insert(method); } void addKeywordArgumentsMethod(FuncDecl *method) { keywordArgumentsMethods.insert(method); } void addMethods(const DynamicCallableMethods &other) { argumentsMethods.insert(other.argumentsMethods.begin(), other.argumentsMethods.end()); keywordArgumentsMethods.insert(other.keywordArgumentsMethods.begin(), other.keywordArgumentsMethods.end()); } /// Returns true if type defines either of the @dynamicCallable /// required methods. Returns false iff type does not satisfy @dynamicCallable /// requirements. bool isValid() const { return !argumentsMethods.empty() || !keywordArgumentsMethods.empty(); } }; /// Abstract base class for applying a solution to a SyntacticElementTarget. class SyntacticElementTargetRewriter { public: virtual Solution &getSolution() const = 0; virtual DeclContext *&getCurrentDC() const = 0; virtual void addLocalDeclToTypeCheck(Decl *D) = 0; [[nodiscard]] virtual std::optional rewriteTarget(SyntacticElementTarget target) = 0; virtual ~SyntacticElementTargetRewriter() = default; }; /// Retrieve the closure type from the constraint system. struct GetClosureType { ConstraintSystem &cs; Type operator()(const AbstractClosureExpr *expr) const; }; /// Retrieve the closure's preconcurrency status from the constraint system. struct ClosureIsolatedByPreconcurrency { ConstraintSystem &cs; bool operator()(const ClosureExpr *expr) const; }; /// Describes a system of constraints on type variables, the /// solution of which assigns concrete types to each of the type variables. /// Constraint systems are typically generated given an (untyped) expression. class ConstraintSystem { private: ASTContext &Context; SourceRange CurrentRange; public: DeclContext *DC; ConstraintSystemOptions Options; DiagnosticTransaction *diagnosticTransaction; std::optional Timer; friend class Solution; friend class ConstraintFix; friend class OverloadChoice; friend class ConstraintGraph; friend class DisjunctionChoice; friend class Component; friend class FailureDiagnostic; friend class TypeVarBindingProducer; friend class TypeVariableBinding; friend class StepScope; friend class SolverStep; friend class SplitterStep; friend class ComponentStep; friend class TypeVariableStep; friend class ConjunctionStep; friend class ConjunctionElement; friend class RequirementFailure; friend class MissingMemberFailure; friend struct ClosureIsolatedByPreconcurrency; friend class SolverTrail; /// Expressions that are known to be unevaluated. /// Note: this is only used to support ObjCSelectorExpr at the moment. llvm::SmallPtrSet UnevaluatedRootExprs; private: bool PreparingOverload = false; /// A constraint that has failed along the current solver path. /// Do not set directly, call \c recordFailedConstraint instead. Constraint *failedConstraint = nullptr; /// The set of expressions for which we have generated constraints. llvm::SetVector InputExprs; /// The number of input expressions whose parents and depths have /// been entered into \c ExprWeights. unsigned NumInputExprsInWeights = 0; llvm::DenseMap> ExprWeights; /// Allocator used for data that is local to this constraint system. llvm::BumpPtrAllocator Allocator; /// Arena used for memory management of constraint-checker-related /// allocations. ConstraintCheckerArenaRAII Arena; /// Counter for type variables introduced. unsigned TypeCounter = 0; /// The number of changes recorded in the trail so far during the /// solution of this constraint system. /// /// This is a rough proxy for how much work the solver did. unsigned NumTrailSteps = 0; /// The number of scopes created so far during the solution /// of this constraint system. /// /// A new scope is created every time we attempt a type variable /// binding or explore an option in a disjunction. /// /// This is a measure of complexity of the solution space. unsigned NumSolverScopes = 0; /// High-water mark of measured memory usage in any sub-scope we /// explored. size_t MaxMemory = 0; /// Flag to indicate to the solver that the system is in invalid /// state and it shouldn't proceed but instead produce a fallback /// diagnostic. bool InvalidState = false; /// Cached member lookups. llvm::DenseMap, std::optional> MemberLookups; /// Folding set containing all of the locators used in this /// constraint system. llvm::FoldingSetVector ConstraintLocators; /// The overload sets that have been resolved along the current path. llvm::DenseMap ResolvedOverloads; /// The current fixed score for this constraint system and the (partial) /// solution it represents. Score CurrentScore; llvm::SetVector TypeVariables; /// Maps expressions to types for choosing a favored overload /// type in a disjunction constraint. llvm::DenseMap FavoredTypes; /// Maps discovered closures to their types inferred /// from declared parameters/result and body. /// /// This is a MapVector because contractEdges() iterates over it and /// may depend on order. llvm::MapVector ClosureTypes; /// Maps closures and local functions to the pack expansion expressions they /// capture. llvm::MapVector> CapturedExpansions; /// Maps expressions for implied results (e.g implicit 'then' statements, /// implicit 'return' statements in single expression body closures) to their /// result kind. llvm::DenseMap ImpliedResults; /// This is a *global* list of all result builder bodies that have /// been determined to be incorrect by failing constraint generation. /// /// Tracking this information is useful to avoid producing duplicate /// diagnostics when result builder has multiple overloads. llvm::SmallDenseSet InvalidResultBuilderBodies; /// The *global* set of all functions that have a particular result builder /// applied. /// /// The value here is `$__builderSelf` variable and a transformed body. llvm::DenseMap, std::pair> BuilderTransformedBodies; /// Arguments after the code completion token that were thus ignored (i.e. /// assigned fresh type variables) for type checking. llvm::SetVector IgnoredArguments; /// Maps node types used within all portions of the constraint /// system, instead of directly using the types on the /// nodes themselves. This allows us to typecheck and /// run through various diagnostics passes without actually mutating /// the types on the nodes. llvm::DenseMap NodeTypes; /// Maps components in a key path expression to their type. Needed because /// KeyPathExpr + Index isn't an \c ASTNode and thus can't be stored in \c /// NodeTypes. llvm::DenseMap, Type> KeyPathComponentTypes; /// Maps a key path root, value, and decl context to the key path expression. llvm::DenseMap> KeyPaths; /// Maps AST entries to their targets. llvm::DenseMap targets; /// Contextual type information for expressions that are part of this /// constraint system. The second type, if valid, contains the type as it /// should appear in actual constraint. This will have unbound generic types /// opened, placeholder types converted to type variables, etc. llvm::DenseMap> contextualTypes; /// Information about each case label item tracked by the constraint system. llvm::SmallDenseMap caseLabelItems; /// Keep track of all of the potential throw sites. /// FIXME: This data structure should be replaced with something that /// is, in effect, a multimap-vector. std::vector> potentialThrowSites; /// A map of expressions to the ExprPatterns that they are being solved as /// a part of. llvm::SmallDenseMap exprPatterns; /// The set of parameters that have been inferred to be 'isolated'. llvm::SmallDenseSet isolatedParams; /// The set of closures that have been inferred to be "isolated by /// preconcurrency". llvm::SmallDenseSet preconcurrencyClosures; /// Maps closure parameters to type variables. llvm::DenseMap OpenedParameterTypes; /// The set of constraint restrictions used to reach the /// current constraint system. /// /// Constraint restrictions help describe which path the solver took when /// there are multiple ways in which one type could convert to another, e.g., /// given class types A and B, the solver might choose either a superclass /// conversion or a user-defined conversion. llvm::DenseMap, ConversionRestrictionKind> ConstraintRestrictions; /// The set of fixes applied to make the solution work. llvm::SmallSetVector Fixes; /// The set of remembered disjunction choices used to reach /// the current constraint system. llvm::SmallDenseMap DisjunctionChoices; /// A map from applied disjunction constraints to the corresponding /// argument function type. llvm::SmallDenseMap AppliedDisjunctions; /// For locators associated with call expressions, the trailing closure /// matching rule and parameter bindings that were applied. llvm::DenseMap argumentMatchingChoices; /// The worklist of "active" constraints that should be revisited /// due to a change. ConstraintList ActiveConstraints; /// The list of "inactive" constraints that still need to be solved, /// but will not be revisited until one of their inputs changes. ConstraintList InactiveConstraints; /// The constraint graph. ConstraintGraph CG; /// Information about the remaining disjunctions we have yet to attempt /// in this path. llvm::DenseMap RemainingDisjunctions; /// A mapping from constraint locators to the set of opened types associated /// with that locator. llvm::SmallDenseMap, 4> OpenedTypes; /// A dictionary of all conformances that have been looked up by the solver. llvm::DenseMap, ProtocolConformanceRef> Conformances; /// A cache for unavailability checks peformed by the solver. llvm::DenseMap, bool> UnavailableDecls; /// The list of all generic requirements fixed along the current /// solver path. using FixedRequirement = std::tuple; llvm::DenseSet FixedRequirements; bool isFixedRequirement(ConstraintLocator *reqLocator, Type requirementTy); /// Add a fixed requirement and record a change to the trail. void recordFixedRequirement(ConstraintLocator *reqLocator, Type requirementTy); /// Primitive form used when applying solution. void recordFixedRequirement(GenericTypeParamType *paramTy, unsigned reqKind, Type reqTy); /// Called to undo the above change. void removeFixedRequirement(GenericTypeParamType *paramTy, unsigned reqKind, Type reqTy); /// A mapping from constraint locators to the opened existential archetype /// used for the 'self' of an existential type. llvm::SmallDenseMap OpenedExistentialTypes; llvm::SmallDenseMap OpenedPackExpansionTypes; llvm::SmallDenseMap PackExpansionEnvironments; llvm::SmallDenseMap PackElementExpansions; /// The set of functions that have been transformed by a result builder. llvm::MapVector resultBuilderTransformed; /// A mapping from the constraint locators for references to various /// names (e.g., member references, normal name references, possible /// constructions) to the argument lists for the call to that locator. llvm::DenseMap ArgumentLists; public: /// A map from argument expressions to their applied property wrapper expressions. llvm::SmallDenseMap, 4> appliedPropertyWrappers; /// The locators of \c Defaultable constraints whose defaults were used. llvm::DenseSet DefaultedConstraints; void recordDefaultedConstraint(ConstraintLocator *locator) { bool inserted = DefaultedConstraints.insert(locator).second; if (inserted) { if (solverState) recordChange(SolverTrail::Change::RecordedDefaultedConstraint(locator)); } } /// A cache that stores the @dynamicCallable required methods implemented by /// types. llvm::DenseMap DynamicCallableCache; /// A cache of implicitly generated dot-member expressions and argument lists /// for some `.callAsFunction` calls. The key here is "base" locator for /// the `.callAsFunction` member reference. llvm::SmallDenseMap ImplicitCallAsFunctions; /// The set of conformances synthesized during solving (i.e. for /// ad-hoc distributed `SerializationRequirement` conformances). llvm::DenseMap SynthesizedConformances; private: /// Describes the current solver state. struct SolverState { SolverState(ConstraintSystem &cs, FreeTypeVariableBinding allowFreeTypeVariables); ~SolverState(); /// The constraint system. ConstraintSystem &CS; FreeTypeVariableBinding AllowFreeTypeVariables; /// Return current depth of solution stack for debug printing. unsigned int getCurrentIndent() const { return depth * 2; } /// Maximum depth reached so far in exploring solutions. unsigned maxDepth = 0; /// Whether to record failures or not. bool recordFixes = false; /// A log of changes to the constraint system, representing the /// current path being explored in the solution space. SolverTrail Trail; /// The best solution computed so far. std::optional BestScore; /// The number of the solution attempts we're looking at. unsigned SolutionAttempt; /// The number of fixes in the innermost partial solution scope. unsigned numPartialSolutionFixes = 0; // Statistics #define CS_STATISTIC(Name, Description) unsigned Name = 0; #include "ConstraintSolverStats.def" /// Add new "generated" constraint along the current solver path. /// /// \param constraint The newly generated constraint. void addGeneratedConstraint(Constraint *constraint) { Trail.recordChange(SolverTrail::Change::GeneratedConstraint(constraint)); } /// Update statistics when a scope begins. unsigned beginScope(); /// Update statistics when a scope ends. void endScope(unsigned scopeNumber, uint64_t startTrailSteps, uint64_t endTrailSteps); /// Check whether constraint system is allowed to form solutions /// even with unbound type variables present. bool allowsFreeTypeVariables() const { return AllowFreeTypeVariables != FreeTypeVariableBinding::Disallow; } /// Disable the given constraint; this change will be rolled back /// when we exit the current solver scope. void disableConstraint(Constraint *constraint) { ASSERT(!constraint->isDisabled()); constraint->setDisabled(); Trail.recordChange(SolverTrail::Change::DisabledConstraint(constraint)); } /// Favor the given constraint; this change will be rolled back /// when we exit the current solver scope. void favorConstraint(Constraint *constraint) { assert(!constraint->isFavored()); constraint->setFavored(); Trail.recordChange(SolverTrail::Change::FavoredConstraint(constraint)); } private: /// Depth of the solution stack. unsigned depth = 0; /// The set of constraints which were active at the time of this state /// creating, it's used to re-activate them on destruction. SmallVector activeConstraints; }; class CacheExprTypes : public ASTWalker { Expr *RootExpr; ConstraintSystem &CS; bool ExcludeRoot; public: CacheExprTypes(Expr *expr, ConstraintSystem &cs, bool excludeRoot) : RootExpr(expr), CS(cs), ExcludeRoot(excludeRoot) {} /// Walk everything in a macro MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::ArgumentsAndExpansion; } PostWalkResult walkToExprPost(Expr *expr) override { if (ExcludeRoot && expr == RootExpr) { assert(!expr->getType() && "Unexpected type in root of expression!"); return Action::Continue(expr); } if (expr->getType()) CS.cacheType(expr); if (auto kp = dyn_cast(expr)) for (auto i : indices(kp->getComponents())) if (kp->getComponents()[i].getComponentType()) CS.cacheType(kp, i); return Action::Continue(expr); } /// Ignore statements. PreWalkResult walkToStmtPre(Stmt *stmt) override { return Action::SkipNode(stmt); } /// Ignore declarations. PreWalkAction walkToDeclPre(Decl *decl) override { return Action::SkipNode(); } }; public: /// Check whether constraint system is in valid state e.g. /// has left-over active/inactive constraints which should /// have been simplified. bool inInvalidState() const { return InvalidState; } /// Cache the types of the given expression and all subexpressions. void cacheExprTypes(Expr *expr) { bool excludeRoot = false; expr->walk(CacheExprTypes(expr, *this, excludeRoot)); } /// Cache the types of the expressions under the given expression /// (but not the type of the given expression). void cacheSubExprTypes(Expr *expr) { bool excludeRoot = true; expr->walk(CacheExprTypes(expr, *this, excludeRoot)); } /// The current solver state. /// /// This will be non-null when we're actively solving the constraint /// system, and carries temporary state related to the current path /// we're exploring. SolverState *solverState = nullptr; void recordChange(SolverTrail::Change change) { ASSERT(!PreparingOverload); solverState->Trail.recordChange(change); } /// Form a locator that can be used to retrieve argument information cached in /// the constraint system for the callee described by the anchor of the /// passed locator. ConstraintLocator *getArgumentInfoLocator(ConstraintLocator *locator); /// Retrieve the argument list that is associated with a call at the given /// locator. ArgumentList *getArgumentList(ConstraintLocator *locator); /// Associate an argument list with a call at a given locator. void associateArgumentList(ConstraintLocator *locator, ArgumentList *args); /// Same as associateArgumentList() except the locator points at the /// argument list itself. Records a change in the trail. void recordArgumentList(ConstraintLocator *locator, ArgumentList *args); /// If the given node is a function expression with a parent ApplyExpr, /// returns the apply, otherwise returns the node itself. ASTNode includingParentApply(ASTNode node); std::optional findSelectedOverloadFor(ConstraintLocator *locator) const { auto result = ResolvedOverloads.find(locator); if (result == ResolvedOverloads.end()) return std::nullopt; return result->second; } std::optional findSelectedOverloadFor(Expr *expr) { // Retrieve the callee locator for this expression, making sure not to // look through applies in order to ensure we only return the "direct" // callee. auto *loc = getConstraintLocator(expr); auto *calleeLoc = getCalleeLocator(loc, /*lookThroughApply*/ false); return findSelectedOverloadFor(calleeLoc); } private: unsigned assignTypeVariableID() { return TypeCounter++; } void incrementScopeCounter(); void incrementLeafScopes(); public: /// Introduces a new solver scope, which any changes to the /// solver state or constraint system are temporary and will be undone when /// this object is destroyed. /// /// struct SolverScope { ConstraintSystem &cs; /// The length of \c TypeVariables at the start of the scope. unsigned startTypeVariables; /// The length of \c Trail at the start of the scope. uint64_t startTrailSteps; /// The scope number of this scope. Set when the scope is registered. unsigned scopeNumber : 31; /// A moved-from scope skips doing anything in the destructor. unsigned moved : 1; explicit SolverScope(ConstraintSystem &cs); SolverScope(const SolverScope &) = delete; SolverScope(SolverScope &&other); SolverScope &operator=(const SolverScope &) = delete; SolverScope &operator=(SolverScope &&) = delete; ~SolverScope(); }; ConstraintSystem(DeclContext *dc, ConstraintSystemOptions options, DiagnosticTransaction *diagnosticTransaction = nullptr); ~ConstraintSystem(); /// Retrieve the constraint graph associated with this constraint system. ConstraintGraph &getConstraintGraph() { return CG; } /// Retrieve the AST context. ASTContext &getASTContext() const { return Context; } /// Determine whether this constraint system has any free type /// variables. bool hasFreeTypeVariables(); /// Check whether constraint solver is running in "debug" mode, /// which should output diagnostic information. bool isDebugMode() const { return Options.contains(ConstraintSystemFlags::DebugConstraints); } private: /// Finalize this constraint system; we're done attempting to solve /// it. /// /// \returns the solution. Solution finalize(); /// Apply the given solution to the current constraint system. /// /// This operation is used to take a solution computed based on some /// subset of the constraints and then apply it back to the /// constraint system for further exploration. void replaySolution(const Solution &solution, bool shouldIncreaseScore=true); // FIXME: Perhaps these belong on ConstraintSystem itself. friend std::optional swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType); friend std::optional swift::TypeChecker::typeCheckExpression( SyntacticElementTarget &target, OptionSet options, DiagnosticTransaction *diagnosticTransaction); friend std::optional swift::TypeChecker::typeCheckTarget( SyntacticElementTarget &target, OptionSet options, DiagnosticTransaction *diagnosticTransaction); friend Type swift::TypeChecker::typeCheckParameterDefault(Expr *&, DeclContext *, Type, bool, bool); /// Emit the fixes computed as part of the solution, returning true if we were /// able to emit an error message, or false if none of the fixits worked out. bool applySolutionFixes(const Solution &solution); /// If there is more than one viable solution, attempt /// to pick the best solution and remove all of the rest. /// /// \param solutions The set of solutions to filter. /// /// \param minimize The flag which indicates if the /// set of solutions should be filtered even if there is /// no single best solution, see `findBestSolution` for /// more details. void filterSolutions(SmallVectorImpl &solutions, bool minimize = false) { if (solutions.size() < 2) return; if (auto best = findBestSolution(solutions, minimize)) { if (*best != 0) solutions[0] = std::move(solutions[*best]); solutions.erase(solutions.begin() + 1, solutions.end()); } } /// Retrieve the set of saved type variable bindings, if available. /// /// \returns null when we aren't currently solving the system. SolverTrail *getTrail() const { return solverState ? &solverState->Trail : nullptr; } /// Add a constraint from the subscript base to the root of the key /// path literal to the constraint system. void addKeyPathApplicationRootConstraint(Type root, ConstraintLocatorBuilder locator); public: /// Add a new type variable that was already created. void addTypeVariable(TypeVariableType *typeVar); /// Lookup for a member with the given name which is in the given base type. /// /// This routine caches the results of member lookups in the top constraint /// system, to avoid. /// /// FIXME: This caching should almost certainly be performed at the /// module level, since type checking occurs after import resolution, /// and no new names are introduced after that point. /// /// \returns A reference to the member-lookup result. LookupResult &lookupMember(Type base, DeclNameRef name, SourceLoc loc); /// Retrieve the set of "alternative" literal types that we'll explore /// for a given literal protocol kind. ArrayRef getAlternativeLiteralTypes(KnownProtocolKind kind, SmallVectorImpl &scratch); /// Create a new type variable. TypeVariableType *createTypeVariable(ConstraintLocator *locator, unsigned options, PreparedOverloadBuilder *preparedOverload = nullptr); /// Retrieve the set of active type variables. ArrayRef getTypeVariables() const { return TypeVariables.getArrayRef(); } /// Whether the given type variable is active in the constraint system at /// the moment. bool isActiveTypeVariable(TypeVariableType *typeVar) const { return TypeVariables.count(typeVar) > 0; } bool containsIDEInspectionTarget(ASTNode node) const; bool containsIDEInspectionTarget(const ArgumentList *args) const; /// Marks the argument \p Arg as being ignored because it occurs after the /// code completion token. This assumes that the argument is not type checked /// (by assigning it a fresh type variable) and prevents fixes from being /// generated for this argument. void markArgumentIgnoredForCodeCompletion(Expr *Arg) { IgnoredArguments.insert(Arg); } /// Whether the argument \p Arg occurs after the code completion token and /// thus should be ignored and not generate any fixes. bool isArgumentIgnoredForCodeCompletion(Expr *Arg) const { return IgnoredArguments.count(Arg) > 0 && isForCodeCompletion(); } /// Whether the constraint system has ignored any arguments for code /// completion, i.e. whether there is an expression for which /// \c isArgumentIgnoredForCodeCompletion returns \c true. bool hasArgumentsIgnoredForCodeCompletion() const { return !IgnoredArguments.empty(); } /// Record an implied result for a ReturnStmt or ThenStmt. void recordImpliedResult(Expr *E, ImpliedResultKind kind) { ASSERT(E); auto inserted = ImpliedResults.insert({E, kind}).second; ASSERT(inserted && "Duplicate implied result?"); if (solverState) recordChange(SolverTrail::Change::RecordedImpliedResult(E)); } /// Undo the above change. void removeImpliedResult(Expr *E) { bool erased = ImpliedResults.erase(E); ASSERT(erased); } /// Whether the given expression is the implied result for either a ReturnStmt /// or ThenStmt, and if so, the kind of implied result. std::optional isImpliedResult(Expr *E) const { auto result = ImpliedResults.find(E); if (result == ImpliedResults.end()) return std::nullopt; return result->second; } void setClosureType(const ClosureExpr *closure, FunctionType *type) { ASSERT(closure); ASSERT(type); bool inserted = ClosureTypes.insert({closure, type}).second; ASSERT(inserted); if (solverState) { recordChange(SolverTrail::Change::RecordedClosureType( const_cast(closure))); } } void removeClosureType(const ClosureExpr *closure) { bool erased = ClosureTypes.erase(closure); ASSERT(erased); } FunctionType *getClosureType(const ClosureExpr *closure) const { auto result = getClosureTypeIfAvailable(closure); assert(result); return result; } FunctionType *getClosureTypeIfAvailable(const ClosureExpr *closure) const { auto result = ClosureTypes.find(closure); if (result != ClosureTypes.end()) return result->second; return nullptr; } SmallVector getCapturedExpansions(AnyFunctionRef func) const { auto result = CapturedExpansions.find(func); if (result == CapturedExpansions.end()) return {}; return result->second; } void setCapturedExpansions(AnyFunctionRef func, SmallVector exprs) { assert(CapturedExpansions.count(func) == 0 && "Cannot reset captured expansions"); CapturedExpansions.insert({func, exprs}); } TypeVariableType *getKeyPathValueType(const KeyPathExpr *keyPath) const { auto result = getKeyPathValueTypeIfAvailable(keyPath); assert(result); return result; } TypeVariableType * getKeyPathValueTypeIfAvailable(const KeyPathExpr *keyPath) const { auto result = KeyPaths.find(keyPath); if (result != KeyPaths.end()) return std::get<1>(result->second); return nullptr; } TypeVariableType *getKeyPathRootType(const KeyPathExpr *keyPath) const { auto result = getKeyPathRootTypeIfAvailable(keyPath); assert(result); return result; } TypeVariableType * getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const { auto result = KeyPaths.find(keyPath); if (result != KeyPaths.end()) return std::get<0>(result->second); return nullptr; } TypeBase *getFavoredType(Expr *E) { assert(E != nullptr); return this->FavoredTypes[E]; } void setFavoredType(Expr *E, TypeBase *T) { assert(E != nullptr); this->FavoredTypes[E] = T; } /// Set the type in our type map for the given node, and record the change /// in the trail. /// /// The side tables are used through the expression type checker to avoid /// mutating nodes until we know we have successfully type-checked them. void setType(ASTNode node, Type type, PreparedOverloadBuilder *preparedOverload=nullptr); /// Undo the above change. void restoreType(ASTNode node, Type oldType); /// Check to see if we have a type for a node. bool hasType(ASTNode node) const { ASSERT(!node.isNull() && "Expected non-null node"); return NodeTypes.count(node) > 0; } /// Set the type in our type map for a given expression. The side /// map is used throughout the expression type checker in order to /// avoid mutating expressions until we know we have successfully /// type-checked them. void setType(const KeyPathExpr *KP, unsigned I, Type T); void restoreType(const KeyPathExpr *KP, unsigned I, Type T); bool hasType(const KeyPathExpr *KP, unsigned I) const { ASSERT(KP && "Expected non-null key path parameter!"); return KeyPathComponentTypes.find(std::make_pair(KP, I)) != KeyPathComponentTypes.end(); } /// Get the type for an node. Type getType(ASTNode node) const { ASSERT(!node.isNull() && "Expected non-null node"); auto found = NodeTypes.find(node); ASSERT(found != NodeTypes.end() && "Expected type to have been set!"); return found->second; } Type getType(const KeyPathExpr *KP, unsigned I) const { ASSERT(KP && "Expected non-null key path parameter!"); auto found = KeyPathComponentTypes.find(std::make_pair(KP, I)); ASSERT(found != KeyPathComponentTypes.end() && "Expected type to have been set!"); return found->second; } /// Retrieve the type of the node, if known. Type getTypeIfAvailable(ASTNode node) const { auto known = NodeTypes.find(node); if (known == NodeTypes.end()) return Type(); return known->second; } /// Cache the type of the expression argument and return that same /// argument. template T *cacheType(T *E) { assert(E->getType() && "Expected a type!"); setType(E, E->getType()); return E; } /// Cache the type of the expression argument and return that same /// argument. KeyPathExpr *cacheType(KeyPathExpr *E, unsigned I) { auto componentTy = E->getComponents()[I].getComponentType(); assert(componentTy && "Expected a type!"); setType(E, I, componentTy); return E; } void setContextualInfo(ASTNode node, ContextualTypeInfo info) { ASSERT(bool(node) && "Expected non-null expression!"); bool inserted = contextualTypes.insert({node, {info, Type()}}).second; ASSERT(inserted); if (solverState) recordChange(SolverTrail::Change::RecordedContextualInfo(node)); } void removeContextualInfo(ASTNode node) { bool erased = contextualTypes.erase(node); ASSERT(erased); } std::optional getContextualTypeInfo(ASTNode node) const { auto known = contextualTypes.find(node); if (known == contextualTypes.end()) return std::nullopt; return known->second.first; } /// Gets the contextual type recorded for an AST node. When fetching a type /// for use in constraint solving, \c forConstraint should be set to \c true, /// which will ensure that unbound generics have been opened and placeholder /// types have been converted to type variables, etc. Type getContextualType(ASTNode node, bool forConstraint) { if (forConstraint) { auto known = contextualTypes.find(node); if (known == contextualTypes.end()) return Type(); // If we've already computed a type for use in the constraint system, // use that. if (known->second.second) return known->second.second; // Otherwise, compute a type that can be used in a constraint and record // it. auto info = known->second.first; auto *locator = getConstraintLocator( node, LocatorPathElt::ContextualType(info.purpose)); known->second.second = replaceInferableTypesWithTypeVars(info.getType(), locator); return known->second.second; } else { auto result = getContextualTypeInfo(node); if (result) return result->getType(); return Type(); } } TypeLoc getContextualTypeLoc(ASTNode node) const { auto result = getContextualTypeInfo(node); if (result) return result->typeLoc; return TypeLoc(); } ContextualTypePurpose getContextualTypePurpose(ASTNode node) const { auto result = getContextualTypeInfo(node); if (result) return result->purpose; return CTP_Unused; } void setTargetFor(SyntacticElementTargetKey key, SyntacticElementTarget target); void removeTargetFor(SyntacticElementTargetKey key); std::optional getTargetFor(SyntacticElementTargetKey key) const; std::optional getAppliedResultBuilderTransform(AnyFunctionRef fn) const { auto transformed = resultBuilderTransformed.find(fn); if (transformed != resultBuilderTransformed.end()) return transformed->second; return std::nullopt; } void setBuilderTransformedBody(AnyFunctionRef fn, NominalTypeDecl *builder, NullablePtr builderSelf, NullablePtr body) { assert(builder->getAttrs().hasAttribute()); assert(body); assert(builderSelf); auto existing = BuilderTransformedBodies.insert( {{fn, builder}, {builderSelf.get(), body.get()}}); assert(existing.second && "Duplicate result builder transform"); (void)existing; } std::optional> getBuilderTransformedBody(AnyFunctionRef fn, NominalTypeDecl *builder) const { auto result = BuilderTransformedBodies.find({fn, builder}); if (result == BuilderTransformedBodies.end()) return std::nullopt; return result->second; } void setCaseLabelItemInfo(const CaseLabelItem *item, CaseLabelItemInfo info) { ASSERT(item); bool inserted = caseLabelItems.insert({item, info}).second; ASSERT(inserted); if (solverState) { recordChange(SolverTrail::Change::RecordedCaseLabelItemInfo( const_cast(item))); } } void removeCaseLabelItemInfo(const CaseLabelItem *item) { bool erased = caseLabelItems.erase(item); ASSERT(erased); } /// Record a given ExprPattern as the parent of its sub-expression. void setExprPatternFor(Expr *E, ExprPattern *EP) { ASSERT(E); ASSERT(EP); bool inserted = exprPatterns.insert({E, EP}).second; ASSERT(inserted); if (solverState) recordChange(SolverTrail::Change::RecordedExprPattern(E)); } /// Record a given ExprPattern as the parent of its sub-expression. void removeExprPatternFor(Expr *E) { bool erased = exprPatterns.erase(E); ASSERT(erased); } std::optional getCaseLabelItemInfo(const CaseLabelItem *item) const { auto known = caseLabelItems.find(item); if (known == caseLabelItems.end()) return std::nullopt; return known->second; } /// Note that there is a potential throw site at the given location. void recordPotentialThrowSite( PotentialThrowSite::Kind kind, Type type, ConstraintLocatorBuilder locator); /// Used by the above to update potentialThrowSites and record a change /// in the trail. void recordPotentialThrowSite(CatchNode catchNode, PotentialThrowSite site); /// Undo the above change. void removePotentialThrowSite(CatchNode catchNode); /// Retrieve the explicit caught error type for the given catch node, without /// attempting any inference. Type getExplicitCaughtErrorType(CatchNode catchNode); /// Determine the caught error type for the given catch node. Type getCaughtErrorType(CatchNode node); /// Retrieve the constraint locator for the given anchor and /// path, uniqued. ConstraintLocator * getConstraintLocator(ASTNode anchor, ArrayRef path, unsigned summaryFlags); /// Retrieve a locator for opening the opaque archetype for the given /// opaque type. ConstraintLocator *getOpenOpaqueLocator( ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl); /// Open the given existential type or existential metatype, recording the /// opened archetype in the constraint system and returning both the opened /// type and opened archetype. std::pair openAnyExistentialType(Type type, ConstraintLocator *locator); /// Update OpenedExistentials and record a change in the trail. void recordOpenedExistentialType(ConstraintLocator *locator, ExistentialArchetypeType *opened, PreparedOverloadBuilder *preparedOverload = nullptr); /// Retrieve the generic environment for the opened element of a given pack /// expansion, or \c nullptr if no environment was recorded yet. GenericEnvironment * getPackExpansionEnvironment(PackExpansionExpr *expr) const; /// Create a new opened element generic environment for the given pack /// expansion. GenericEnvironment * createPackExpansionEnvironment(PackExpansionExpr *expr, CanGenericTypeParamType shapeParam); /// Update PackExpansionEnvironments and record a change in the trail. void recordPackExpansionEnvironment(PackExpansionExpr *expr, GenericEnvironment *env); /// Undo the above change. void removePackExpansionEnvironment(PackExpansionExpr *expr) { bool erased = PackExpansionEnvironments.erase(expr); ASSERT(erased); } /// Get the pack expansion expr for the given pack element. PackExpansionExpr * getPackElementExpansion(PackElementExpr *packElement) const; /// Associate a pack element with a given pack expansion, and record the /// change in the trail. void recordPackElementExpansion(PackElementExpr *packElement, PackExpansionExpr *packExpansion); /// Undo the above change. void removePackElementExpansion(PackElementExpr *packElement) { bool erased = PackElementExpansions.erase(packElement); ASSERT(erased); } /// Retrieve the constraint locator for the given anchor and /// path, uniqued and automatically infer the summary flags ConstraintLocator * getConstraintLocator(ASTNode anchor, ArrayRef path); /// Retrieve the constraint locator for the given anchor and /// an empty path, uniqued. ConstraintLocator *getConstraintLocator(ASTNode anchor) { return getConstraintLocator(anchor, {}, 0); } /// Retrieve the constraint locator for the given anchor and /// path element. ConstraintLocator * getConstraintLocator(ASTNode anchor, ConstraintLocator::PathElement pathElt) { return getConstraintLocator(anchor, llvm::ArrayRef(pathElt), pathElt.getNewSummaryFlags()); } /// Extend the given constraint locator with a path element. ConstraintLocator * getConstraintLocator(ConstraintLocator *locator, ConstraintLocator::PathElement pathElt) { ConstraintLocatorBuilder builder(locator); return getConstraintLocator(builder.withPathElement(pathElt)); } /// Extend the given constraint locator with an array of path elements. ConstraintLocator * getConstraintLocator(ConstraintLocator *locator, ArrayRef newElts); /// Retrieve the locator described by a given builder extended by an array of /// path elements. ConstraintLocator * getConstraintLocator(const ConstraintLocatorBuilder &builder, ArrayRef newElts); /// Retrieve the constraint locator described by the given /// builder. ConstraintLocator * getConstraintLocator(const ConstraintLocatorBuilder &builder); /// Lookup and return parent associated with given expression. Expr *getParentExpr(Expr *expr) { if (auto result = getExprDepthAndParent(expr)) return result->second; return nullptr; } Expr *getSemanticsProvidingParentExpr(Expr *expr) { while (auto *parent = getParentExpr(expr)) { if (parent->getSemanticsProvidingExpr() == parent) return parent; expr = parent; } return nullptr; } /// Retrieve the depth of the given expression. std::optional getExprDepth(Expr *expr) { if (auto result = getExprDepthAndParent(expr)) return result->first; return std::nullopt; } /// Retrieve the depth and parent expression of the given expression. std::optional> getExprDepthAndParent(Expr *expr); /// Returns a locator describing the callee for the anchor of a given locator. /// /// - For an unresolved dot/member anchor, this will be a locator describing /// the member. /// /// - For a subscript anchor, this will be a locator describing the subscript /// member. /// /// - For a key path anchor with a property/subscript component path element, /// this will be a locator describing the decl referenced by the component. /// /// - For a function application anchor, this will be a locator describing the /// 'direct callee' of the call. For example, for the expression \c x.foo?() /// the returned locator will describe the member \c foo. /// /// Note that because this function deals with the anchor, given a locator /// anchored on \c functionA(functionB()) with path elements pointing to the /// argument \c functionB(), the returned callee locator will describe /// \c functionA rather than \c functionB. /// /// \param locator The input locator. /// \param lookThroughApply Whether to look through applies. If false, a /// callee locator will only be returned for a direct reference such as /// \c x.foo rather than \c x.foo(). /// \param getType The callback to fetch a type for given expression. /// \param simplifyType The callback to attempt to resolve any type /// variables which appear in the given type. /// \param getOverloadFor The callback to fetch overload for a given /// locator if available. ConstraintLocator *getCalleeLocator( ConstraintLocator *locator, bool lookThroughApply, llvm::function_ref getType, llvm::function_ref simplifyType, llvm::function_ref(ConstraintLocator *)> getOverloadFor); ConstraintLocator *getCalleeLocator(ConstraintLocator *locator, bool lookThroughApply = true) { return getCalleeLocator( locator, lookThroughApply, [&](Expr *expr) -> Type { return getType(expr); }, [&](Type type) -> Type { return simplifyType(type)->getRValueType(); }, [&](ConstraintLocator *locator) -> std::optional { return findSelectedOverloadFor(locator); }); } /// Determine whether the callee for the given locator is marked as /// `@preconcurrency`. bool hasPreconcurrencyCallee(ConstraintLocatorBuilder locator); /// Determine whether the given declaration is unavailable from the /// current context. bool isDeclUnavailable(const Decl *D, ConstraintLocator *locator = nullptr) const; /// Determine whether the given conformance is unavailable from the /// current context. bool isConformanceUnavailable(ProtocolConformanceRef conformance, ConstraintLocator *locator = nullptr) const; public: /// Whether we should attempt to fix problems. bool shouldAttemptFixes() const { if (!(Options & ConstraintSystemFlags::AllowFixes)) return false; return !solverState || solverState->recordFixes; } bool inSalvageMode() const { return solverState && solverState->recordFixes; } ArrayRef getFixes() const { return Fixes.getArrayRef(); } bool shouldSuppressDiagnostics() const { return Options.contains(ConstraintSystemFlags::SuppressDiagnostics); } /// Whether we are solving to determine the possible types of a /// \c CodeCompletionExpr. bool isForCodeCompletion() const { return Options.contains(ConstraintSystemFlags::ForCodeCompletion); } /// Log and record the application of the fix. Return true iff any /// subsequent solution would be worse than the best known solution. bool recordFix(ConstraintFix *fix, unsigned impact = 1, PreparedOverloadBuilder *preparedOverload = nullptr); void recordPotentialHole(TypeVariableType *typeVar); void recordAnyTypeVarAsPotentialHole(Type type); /// Record all unbound type variables that occur in the given type /// as being bound to "hole" type represented by \c PlaceholderType /// in this constraint system. /// /// \param type The type on which to holeify. void recordTypeVariablesAsHoles(Type type); /// Add a MatchCallArgumentResult and record the change in the trail. void recordMatchCallArgumentResult(ConstraintLocator *locator, MatchCallArgumentResult result); /// Record a new implicit `callAsFunction` call for a split argument list /// e.g `T(...) {}` -> `T(...).callAsFunction {}` /// /// \param root The member access to \c callAsFunction /// \param baseArgs The new arguments for the base \c T(...) call. The /// arguments for the \c callAsFunction call are recorded on the \c root void recordImplicitCallAsFunction(ConstraintLocator *locator, UnresolvedDotExpr *root, ArgumentList *baseArgs); /// Record root, value, and declContext of keypath expression for use across /// constraint system, and add a change to the trail. void recordKeyPath(const KeyPathExpr *keypath, TypeVariableType *root, TypeVariableType *value, DeclContext *dc); /// Undo the above change. void removeKeyPath(const KeyPathExpr *keypath); /// Walk a closure AST to determine its effects. /// /// \returns a function's extended info describing the effects, as /// determined syntactically. FunctionType::ExtInfo closureEffects(ClosureExpr *expr); /// Determine whether the given context is asynchronous, e.g., an async /// function or closure. bool isAsynchronousContext(DeclContext *dc); /// Determine whether constraint system already has a fix recorded /// for a particular location. bool hasFixFor(ConstraintLocator *locator, std::optional expectedKind = std::nullopt) const { return llvm::any_of( Fixes, [&locator, &expectedKind](const ConstraintFix *fix) { if (fix->getLocator() == locator) { return !expectedKind || fix->getKind() == *expectedKind; } return false; }); } /// Try to salvage the constraint system by applying (speculative) /// fixes. SolutionResult salvage(); /// Mine the active and inactive constraints in the constraint /// system to generate a plausible diagnosis of why the system could not be /// solved. /// /// \param target The solution target whose constraints we're investigating /// for a better diagnostic. /// /// Assuming that this constraint system is actually erroneous, this *always* /// emits an error message. void diagnoseFailureFor(SyntacticElementTarget target); bool diagnoseAmbiguity(ArrayRef solutions); bool diagnoseAmbiguityWithFixes(SmallVectorImpl &solutions); /// Add a constraint to the constraint system. void addConstraint(ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, bool isFavored = false, PreparedOverloadBuilder *preparedOverload = nullptr); /// Add a requirement as a constraint to the constraint system. void addConstraint(Requirement req, ConstraintLocatorBuilder locator, bool isFavored, bool prohibitNonisolatedConformance, PreparedOverloadBuilder *preparedOverload = nullptr); void addApplicationConstraint( FunctionType *appliedFn, Type calleeType, std::optional trailingClosureMatching, DeclContext *useDC, ConstraintLocatorBuilder locator); /// Add the appropriate constraint for a contextual conversion. void addContextualConversionConstraint(Expr *expr, Type conversionType, ContextualTypePurpose purpose, ConstraintLocator *locator, bool shouldOpenOpaqueType = true); /// Add a constraint to the constraint system with an associated fix. void addFixConstraint(ConstraintFix *fix, ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, bool isFavored = false); /// Add a key path application constraint to the constraint system. void addKeyPathApplicationConstraint(Type keypath, Type root, Type value, ConstraintLocatorBuilder locator, bool isFavored = false); /// Add a key path constraint to the constraint system. void addKeyPathConstraint(Type keypath, Type root, Type value, ArrayRef componentTypeVars, ConstraintLocatorBuilder locator, bool isFavored = false); /// Add a new constraint with a restriction on its application. void addRestrictedConstraint(ConstraintKind kind, ConversionRestrictionKind restriction, Type first, Type second, ConstraintLocatorBuilder locator); /// Add a constraint that binds an overload set to a specific choice. void addBindOverloadConstraint(Type boundTy, OverloadChoice choice, ConstraintLocator *locator, DeclContext *useDC) { resolveOverload(choice, useDC, locator, boundTy, /*preparedOverload=*/nullptr); } /// Add a value member constraint to the constraint system. void addValueMemberConstraint(Type baseTy, DeclNameRef name, Type memberTy, DeclContext *useDC, FunctionRefInfo functionRefInfo, ArrayRef outerAlternatives, ConstraintLocatorBuilder locator) { assert(baseTy); assert(memberTy); assert(name); assert(useDC); switch (simplifyMemberConstraint( ConstraintKind::ValueMember, baseTy, name, memberTy, useDC, functionRefInfo, outerAlternatives, TMF_GenerateConstraints, locator)) { case SolutionKind::Unsolved: llvm_unreachable("Unsolved result when generating constraints!"); case SolutionKind::Solved: break; case SolutionKind::Error: if (shouldRecordFailedConstraint()) { recordFailedConstraint(Constraint::createMemberOrOuterDisjunction( *this, ConstraintKind::ValueMember, baseTy, memberTy, name, useDC, functionRefInfo, outerAlternatives, getConstraintLocator(locator))); } break; } } /// Add a value member constraint for an UnresolvedMemberRef /// to the constraint system. void addUnresolvedValueMemberConstraint(Type baseTy, DeclNameRef name, Type memberTy, DeclContext *useDC, FunctionRefInfo functionRefInfo, ConstraintLocatorBuilder locator) { assert(baseTy); assert(memberTy); assert(name); assert(useDC); switch (simplifyMemberConstraint(ConstraintKind::UnresolvedValueMember, baseTy, name, memberTy, useDC, functionRefInfo, /*outerAlternatives=*/{}, TMF_GenerateConstraints, locator)) { case SolutionKind::Unsolved: llvm_unreachable("Unsolved result when generating constraints!"); case SolutionKind::Solved: break; case SolutionKind::Error: if (shouldRecordFailedConstraint()) { recordFailedConstraint( Constraint::createMember(*this, ConstraintKind::UnresolvedValueMember, baseTy, memberTy, name, useDC, functionRefInfo, getConstraintLocator(locator))); } break; } } /// Add an explicit conversion constraint (e.g., \c 'x as T'). /// /// \param fromType The type of the expression being converted. /// \param toType The type to convert to. /// \param rememberChoice Whether the conversion disjunction should record its /// choice. /// \param locator The locator. /// \param compatFix A compatibility fix that can be applied if the conversion /// fails. void addExplicitConversionConstraint(Type fromType, Type toType, RememberChoice_t rememberChoice, ConstraintLocatorBuilder locator, ConstraintFix *compatFix = nullptr); /// Given a tuple with a single unlabeled element that represents a pack /// expansion (either directly via \c PackExpansionType or through a type /// variable constrained to a pack expansion), materialize the pack expansion /// and return its pattern type as a result. The result is a type /// variable because element of the tuple is not required to be resolved at /// the time of the call and operation is delayed until the element is /// sufficiently resolved (see \c simplifyMaterializePackExpansionConstraint) /// /// \param tupleType A tuple with a single unlabeled element that represents a /// pack expansion. /// \param locator The locator. /// /// \returns A type variable type that represents the pattern type of the pack /// expansion. TypeVariableType * addMaterializePackExpansionConstraint(Type tupleType, ConstraintLocatorBuilder locator); /// Add a disjunction constraint. void addDisjunctionConstraint(ArrayRef constraints, ConstraintLocatorBuilder locator, RememberChoice_t rememberChoice = ForgetChoice) { auto constraint = Constraint::createDisjunction(*this, constraints, getConstraintLocator(locator), rememberChoice); addUnsolvedConstraint(constraint); } /// Whether we should record the failure of a constraint. bool shouldRecordFailedConstraint() const { // If we're debugging, always note a failure so we can print it out. if (isDebugMode()) return true; // Otherwise, only record it if we don't already have a failed constraint. // This avoids allocating unnecessary constraints. return !failedConstraint; } /// Note that a particular constraint has failed, setting \c failedConstraint /// if necessary. void recordFailedConstraint(Constraint *constraint) { assert(!constraint->isActive()); if (!failedConstraint) failedConstraint = constraint; if (isDebugMode()) { auto &log = llvm::errs(); log.indent(solverState ? solverState->getCurrentIndent() + 4 : 0) << "(failed constraint "; constraint->print(log, &getASTContext().SourceMgr); log << ")\n"; } } /// Remove a constraint from the system that has failed, setting /// \c failedConstraint if necessary. void retireFailedConstraint(Constraint *constraint) { retireConstraint(constraint); recordFailedConstraint(constraint); } /// Add a newly-generated constraint that is known not to be solvable /// right now. void addUnsolvedConstraint(Constraint *constraint) { // We couldn't solve this constraint; add it to the pile. InactiveConstraints.push_back(constraint); // Add this constraint to the constraint graph. CG.addConstraint(constraint); if (isDebugMode() && solverState) { auto &log = llvm::errs(); log.indent(solverState->getCurrentIndent() + 4) << "(added constraint: "; constraint->print(log, &getASTContext().SourceMgr, solverState->getCurrentIndent() + 4); log << ")\n"; } // Record this as a newly-generated constraint. if (solverState) solverState->addGeneratedConstraint(constraint); } /// Remove an inactive constraint from the current constraint graph. void removeInactiveConstraint(Constraint *constraint) { CG.removeConstraint(constraint); auto where = InactiveConstraints.erase(constraint); if (solverState) recordChange(SolverTrail::Change::RetiredConstraint(where, constraint)); if (isDebugMode() && solverState) { auto &log = llvm::errs(); log.indent(solverState->getCurrentIndent() + 4) << "(removed constraint: "; constraint->print(log, &getASTContext().SourceMgr, solverState->getCurrentIndent() + 4); log << ")\n"; } } /// Transfer given constraint from to active list /// for solver to attempt its simplification. void activateConstraint(Constraint *constraint) { assert(!constraint->isActive() && "Constraint is already active"); ActiveConstraints.splice(ActiveConstraints.end(), InactiveConstraints, constraint); constraint->setActive(true); } void deactivateConstraint(Constraint *constraint) { assert(constraint->isActive() && "Constraint is already inactive"); InactiveConstraints.splice(InactiveConstraints.end(), ActiveConstraints, constraint); constraint->setActive(false); } void retireConstraint(Constraint *constraint) { if (constraint->isActive()) deactivateConstraint(constraint); removeInactiveConstraint(constraint); } /// Note that this constraint is "favored" within its disjunction, and /// should be tried first to the exclusion of non-favored constraints in /// the same disjunction. void favorConstraint(Constraint *constraint) { if (constraint->isFavored()) return; if (solverState) { solverState->favorConstraint(constraint); } else { constraint->setFavored(); } } /// Retrieve the list of inactive constraints. ConstraintList &getConstraints() { return InactiveConstraints; } /// The worklist of "active" constraints that should be revisited /// due to a change. ConstraintList &getActiveConstraints() { return ActiveConstraints; } /// Retrieve the representative of the equivalence class containing /// this type variable. TypeVariableType *getRepresentative(TypeVariableType *typeVar) const; /// Find if the given type variable is representative for a type /// variable which last locator path element is of the specified kind. /// If true returns the type variable which it is the representative for. TypeVariableType * isRepresentativeFor(TypeVariableType *typeVar, ConstraintLocator::PathElementKind kind) const; /// Gets the the type of the projection if the decl has an associated property /// wrapper with a projectedValue. Type getPropertyWrapperProjectionType(SelectedOverload resolvedOverload); /// Gets the type of the backing storage if the decl has an associated /// property wrapper. Type getPropertyWrapperBackingType(SelectedOverload resolvedOverload); /// Gets the type of a wrapped property if resolved overload has /// a decl which is the backing storage for a property wrapper. Type getWrappedPropertyType(SelectedOverload resolvedOverload); /// Merge the equivalence sets of the two type variables. /// /// Note that both \c typeVar1 and \c typeVar2 must be the /// representatives of their equivalence classes, and must be /// distinct. void mergeEquivalenceClasses(TypeVariableType *typeVar1, TypeVariableType *typeVar2, bool updateWorkList); /// Flags that direct type matching. enum TypeMatchFlags { /// Indicates that we are in a context where we should be /// generating constraints for any unsolvable problems. /// /// This flag is automatically introduced when type matching destructures /// a type constructor (tuple, function type, etc.), solving that /// constraint while potentially generating others. TMF_GenerateConstraints = 0x01, /// Indicates that we are applying a fix. TMF_ApplyingFix = 0x02, /// Indicates that we are attempting a possible type for /// a type variable. TMF_BindingTypeVariable = 0x04, /// Indicates that the solver is matching one of the /// generic argument pairs as part of matching two generic types. TMF_MatchingGenericArguments = 0x08, }; /// Options that govern how type matching should proceed. using TypeMatchOptions = OptionSet; /// Retrieve the fixed type corresponding to the given type variable, /// or a null type if there is no fixed type. Type getFixedType(TypeVariableType *typeVar) const; /// Retrieve the fixed type corresponding to a given type variable, /// recursively, until we hit something that isn't a type variable /// or a type variable that doesn't have a fixed type. /// /// \param type The type to simplify. /// /// \param wantRValue Whether this routine should look through /// lvalues at each step. Type getFixedTypeRecursive(Type type, bool wantRValue) { TypeMatchOptions flags = std::nullopt; return getFixedTypeRecursive(type, flags, wantRValue); } /// Retrieve the fixed type corresponding to a given type variable, /// recursively, until we hit something that isn't a type variable /// or a type variable that doesn't have a fixed type. /// /// \param type The type to simplify. /// /// \param flags When simplifying one of the types that is part of a /// constraint we are examining, the set of flags that governs the /// simplification. The set of flags may be both queried and mutated. /// /// \param wantRValue Whether this routine should look through /// lvalues at each step. Type getFixedTypeRecursive(Type type, TypeMatchOptions &flags, bool wantRValue); /// Determine whether the given type variable occurs within the given type. /// /// This routine assumes that the type has already been fully simplified. /// /// \param involvesOtherTypeVariables if non-null, records whether any other /// type variables are present in the type. static bool typeVarOccursInType(TypeVariableType *typeVar, Type type, bool *involvesOtherTypeVariables = nullptr); /// Given the fact that contextual type is now available for the type /// variable representing one of the closures, let's set pre-determined /// closure type and generate constraints for its body, iff it's a /// single-statement closure. /// /// \param typeVar The type variable representing a function type of the /// closure expression. /// \param contextualType The contextual type this closure would be /// converted to. /// \param locator The locator associated with contextual type. /// /// \returns `true` if it was possible to generate constraints for /// the body and assign fixed type to the closure, `false` otherwise. bool resolveClosure(TypeVariableType *typeVar, Type contextualType, ConstraintLocatorBuilder locator); /// Used by the above to update isolatedParams and record a change in /// the trail. void recordIsolatedParam(ParamDecl *param); /// Undo the above change. void removeIsolatedParam(ParamDecl *param); /// Used by the above to update preconcurrencyClosures and record a change in /// the trail. void recordPreconcurrencyClosure(const ClosureExpr *closure); /// Undo the above change. void removePreconcurrencyClosure(const ClosureExpr *closure); /// Given the fact that contextual type is now available for the type /// variable representing a pack expansion type, let's resolve the expansion. /// /// \param typeVar The type variable representing pack expansion type. /// \param contextualType The contextual type this pack expansion variable /// would be bound/equated to. /// /// \returns `true` if pack expansion has been resolved, `false` otherwise. bool resolvePackExpansion(TypeVariableType *typeVar, Type contextualType); /// Bind tap expression to the given contextual type and generate /// constraints for its body. /// /// \param typeVar The type variable representing the tap expression. /// \param contextualType The contextual type this tap expression /// would be bound to. /// \param locator The locator associated with contextual type. /// /// \returns `true` if it was possible to generate constraints for /// the body and assign fixed type to the tap expression, `false` /// otherwise. bool resolveTapBody(TypeVariableType *typeVar, Type contextualType, ConstraintLocatorBuilder locator); /// Bind key path expression to the given contextual type and generate /// constraints for its requirements. /// /// \param typeVar The type variable representing the key path expression. /// \param contextualType The contextual type this key path expression /// would be bound to. /// \param flags The flags associated with this assignment. /// \param locator The locator associated with contextual type. /// /// \returns `true` if it was possible to generate constraints for /// the requirements and assign fixed type to the key path expression, /// `false` otherwise. bool resolveKeyPath(TypeVariableType *typeVar, Type contextualType, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Assign a fixed type to the given type variable. /// /// \param typeVar The type variable to bind. /// /// \param type The fixed type to which the type variable will be bound. /// /// \param updateState Whether to update the state based on this binding. /// False when we're only assigning a type as part of reconstructing /// a complete solution from partial solutions. /// /// \param notifyBindingInference Whether to notify binding inference about /// the change to this type variable. void assignFixedType(TypeVariableType *typeVar, Type type, bool updateState = true, bool notifyBindingInference = true); /// Update ConstraintRestrictions and record a change in the trail. void addConversionRestriction(Type srcType, Type dstType, ConversionRestrictionKind restriction); /// Called to undo the above change. void removeConversionRestriction(Type srcType, Type dstType); /// Update Fixes and record a change in the trail. void addFix(ConstraintFix *fix); /// Called to undo the above change. void removeFix(ConstraintFix *fix); /// Determine whether the given type is a dictionary and, if so, provide the /// key and value types for the dictionary. static std::optional> isDictionaryType(Type type); /// Determine if the type in question is a Set and, if so, provide the /// element type of the set. static std::optional isSetType(Type t); /// Call Expr::isTypeReference on the given expression, using a /// custom accessor for the type on the expression that reads the /// type from the ConstraintSystem expression type map. bool isTypeReference(Expr *E); /// Call Expr::isIsStaticallyDerivedMetatype on the given /// expression, using a custom accessor for the type on the /// expression that reads the type from the ConstraintSystem /// expression type map. bool isStaticallyDerivedMetatype(Expr *E); /// Call TypeExpr::getInstanceType on the given expression, using a /// custom accessor for the type on the expression that reads the /// type from the ConstraintSystem expression type map. Type getInstanceType(TypeExpr *E); /// Call AbstractClosureExpr::getResultType on the given expression, /// using a custom accessor for the type on the expression that /// reads the type from the ConstraintSystem expression type map. Type getResultType(const AbstractClosureExpr *E); private: /// Introduce the constraints associated with the given type variable /// into the worklist. void addTypeVariableConstraintsToWorkList(TypeVariableType *typeVar); public: /// Coerce the given expression to an rvalue, if it isn't already. Expr *coerceToRValue(Expr *expr); /// Add implicit "load" expressions to the given expression. Expr *addImplicitLoadExpr(Expr *expr); /// "Open" the unbound generic type represented by the given declaration and /// parent type by introducing fresh type variables for generic parameters /// and constructing a bound generic type from these type variables. /// /// \param isTypeResolution Whether we are in the process of resolving a type. /// /// \returns The opened type. Type openUnboundGenericType(GenericTypeDecl *decl, Type parentTy, ConstraintLocatorBuilder locator, bool isTypeResolution, PreparedOverloadBuilder *preparedOverload = nullptr); /// Replace placeholder types with fresh type variables, and unbound generic /// types with bound generic types whose generic args are fresh type /// variables. /// /// \param type The type on which to perform the conversion. /// /// \returns The converted type. Type replaceInferableTypesWithTypeVars(Type type, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload = nullptr); /// "Open" the given type by replacing any occurrences of generic /// parameter types and dependent member types with fresh type variables. /// /// \param type The type to open. /// \param replacements The mapping from generic type parameters to their /// corresponding opened type variables. /// /// \returns The opened type, or \c type if there are no archetypes in it. Type openType(Type type, ArrayRef replacements, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); /// "Open" an opaque archetype type, similar to \c openType. Type openOpaqueType(OpaqueTypeArchetypeType *type, ConstraintLocatorBuilder locator); /// Recurse over the given type and open any opaque archetype types. Type openOpaqueType(Type type, ContextualTypePurpose context, ConstraintLocatorBuilder locator, Decl *ownerDecl); /// "Open" a pack expansion type by replacing it with a type variable, /// opening its pattern and shape types and connecting them to the /// aforementioned variable via special constraints. Type openPackExpansionType(PackExpansionType *expansion, ArrayRef replacements, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); /// Update OpenedPackExpansionTypes and record a change in the trail. void recordOpenedPackExpansionType(PackExpansionType *expansion, TypeVariableType *expansionVar, PreparedOverloadBuilder *preparedOverload = nullptr); /// Undo the above change. void removeOpenedPackExpansionType(PackExpansionType *expansion) { bool erased = OpenedPackExpansionTypes.erase(expansion); ASSERT(erased); } /// "Open" the given function type. /// /// If the function type is non-generic, this is equivalent to calling /// openType(). Otherwise, it calls openGeneric() on the generic /// function's signature first. /// /// \param funcType The function type to open. /// /// \param replacements The mapping from opened types to the type /// variables to which they were opened. /// /// \param outerDC The generic context containing the declaration. /// /// \returns The opened type, or \c type if there are no archetypes in it. FunctionType *openFunctionType(AnyFunctionType *funcType, ConstraintLocatorBuilder locator, SmallVectorImpl &replacements, DeclContext *outerDC, PreparedOverloadBuilder *preparedOverload); /// Open the generic parameter list and its requirements, /// creating type variables for each of the type parameters. void openGeneric(DeclContext *outerDC, GenericSignature signature, ConstraintLocatorBuilder locator, SmallVectorImpl &replacements, PreparedOverloadBuilder *preparedOverload); /// Open the generic parameter list creating type variables for each of the /// type parameters. void openGenericParameters(DeclContext *outerDC, GenericSignature signature, SmallVectorImpl &replacements, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); /// Open a generic parameter into a type variable and record /// it in \c replacements. TypeVariableType *openGenericParameter(GenericTypeParamType *parameter, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); /// Given generic signature open its generic requirements, /// using substitution function, and record them in the /// constraint system for further processing. void openGenericRequirements(DeclContext *outerDC, GenericSignature signature, bool skipProtocolSelfConstraint, ConstraintLocatorBuilder locator, llvm::function_ref subst, PreparedOverloadBuilder *preparedOverload); // Record the given requirement in the constraint system. void openGenericRequirement(DeclContext *outerDC, GenericSignature signature, unsigned index, Requirement requirement, ConstraintLocatorBuilder locator, llvm::function_ref subst, PreparedOverloadBuilder *preparedOverload); /// Update OpenedTypes and record a change in the trail. void recordOpenedType( ConstraintLocator *locator, ArrayRef openedTypes, PreparedOverloadBuilder *preparedOverload = nullptr); /// Record the set of opened types for the given locator. void recordOpenedTypes( ConstraintLocatorBuilder locator, const SmallVectorImpl &replacements, PreparedOverloadBuilder *preparedOverload = nullptr, bool fixmeAllowDuplicates = false); /// Check whether the given type conforms to the given protocol and if /// so return a valid conformance reference. ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *P); /// We memoize the computation in the below. llvm::DenseMap, bool> ConformanceTransitiveForSupertypeCache; /// Suppose we are given a type T with the given conversion behavior, /// and a protocol P, with the following setup: /// - T conv $T0 /// - $T0 conforms P /// The question is, does this imply that T must conform to P? This /// returns true if so, false otherwise. /// /// Also see Subtyping.h, checkTranstiveSupertypeConformance(). bool isConformanceTransitiveForSupertype(ConversionBehavior behavior, ProtocolDecl *proto); /// We memoize the computation in the below. llvm::DenseMap, bool> ConformanceTransitiveForSubtypeCache; /// Suppose we are given a type T with the given conversion behavior, /// and a protocol P, with the following setup: /// - $T0 conv T /// - $T0 conforms P /// The question is, does this imply that T must conform to P? This /// returns true if so, false otherwise. /// /// Also see Subtyping.h, checkTranstiveSubtypeConformance(). bool isConformanceTransitiveForSubtype(ConversionBehavior behavior, ProtocolDecl *proto); /// Wrapper over swift::adjustFunctionTypeForConcurrency that passes along /// the appropriate closure-type and opening extraction functions. FunctionType *adjustFunctionTypeForConcurrency( FunctionType *fnType, Type baseType, ValueDecl *decl, DeclContext *dc, unsigned numApplies, bool isMainDispatchQueue, bool openGlobalActorType, ConstraintLocatorBuilder locator); /// Retrieve the type of a reference to the given value declaration. /// /// For references to polymorphic function types, this routine "opens up" /// the type by replacing each instance of an archetype with a fresh type /// variable. /// /// \returns a description of the type of this declaration reference. DeclReferenceType getTypeOfReference( OverloadChoice choice, DeclContext *useDC, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); /// Retrieve the type of a reference to the given value declaration, /// as a member with a base of the given type. /// /// For references to generic function types or members of generic types, /// this routine "opens up" the type by replacing each instance of a generic /// parameter with a fresh type variable. /// /// \returns a description of the type of this declaration reference. DeclReferenceType getTypeOfMemberReference( OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator, PreparedOverloadBuilder *preparedOverload); /// Retrieve a list of generic parameter types solver has "opened" (replaced /// with a type variable) at the given location. ArrayRef getOpenedTypes(ConstraintLocator *locator) const { auto substitutions = OpenedTypes.find(locator); if (substitutions == OpenedTypes.end()) return {}; return substitutions->second; } private: /// \returns The opened type and the thrown error type. std::pair getTypeOfReferencePre( OverloadChoice choice, DeclContext *useDC, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); DeclReferenceType getTypeOfReferencePost( OverloadChoice choice, DeclContext *useDC, ConstraintLocatorBuilder locator, Type openedType, Type thrownErrorType); /// \returns the opened type and the thrown error type. std::pair getTypeOfMemberReferencePre( OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator, PreparedOverloadBuilder *preparedOverload); DeclReferenceType getTypeOfMemberReferencePost( OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator, Type openedType, Type thrownErrorType); Type getTypeOfMemberTypeReference( Type baseObjTy, TypeDecl *typeDecl, ConstraintLocator *locator, PreparedOverloadBuilder *preparedOverload); /// Return the type-of-reference of the given value. /// /// \param baseType if non-null, return the type of a member reference to /// this value when the base has the given type /// /// \param UseDC The context of the access. Some variables have different /// types depending on where they are used. /// /// \param locator The locator anchored at this value reference, when /// it is a member reference. /// /// \param wantInterfaceType Whether we want the interface type, if available. Type getUnopenedTypeOfReference(VarDecl *value, Type baseType, DeclContext *UseDC, ConstraintLocator *locator, bool wantInterfaceType); std::pair getOpenedStorageType( Type baseTy, AbstractStorageDecl *value, DeclContext *useDC, bool hasAppliedSelf, ArrayRef replacements, ConstraintLocator *locator, PreparedOverloadBuilder *preparedOverload); /// Given the opened type and a pile of information about a member reference, /// determine the reference type of the member reference. Type getMemberReferenceTypeFromOpenedType( Type type, Type baseObjTy, ValueDecl *value, ConstraintLocator *locator, bool hasAppliedSelf, bool isDynamicLookup); /// Add the constraints needed to bind an overload's type variable. void bindOverloadType(const SelectedOverload &overload, Type boundType, ConstraintLocator *locator, DeclContext *useDC); /// Describes a direction of optional wrapping, either increasing optionality /// or decreasing optionality. enum class OptionalWrappingDirection { /// Unwrap an optional type T? to T. Unwrap, /// Promote a type T to optional type T?. Promote }; /// Attempts to find a constraint that involves \p typeVar and satisfies /// \p predicate, looking through optional object constraints if necessary. If /// multiple candidates are found, returns the first one. /// /// \param optionalDirection The direction to travel through optional object /// constraints, either increasing or decreasing optionality. /// /// \param predicate Checks whether a given constraint is the one being /// searched for. The type variable passed is the current representative /// after looking through the optional object constraints. /// /// \returns The constraint found along with the number of optional object /// constraints looked through, or \c None if no constraint was found. std::optional> findConstraintThroughOptionals( TypeVariableType *typeVar, OptionalWrappingDirection optionalDirection, llvm::function_ref predicate); /// Attempt to simplify the set of overloads corresponding to a given /// function application constraint. /// /// \param disjunction The disjunction for the set of overloads. /// /// \param fnTypeVar The type variable that describes the set of /// overloads for the function. /// /// \param argFnType The call signature, which includes the call arguments /// (as the function parameters) and the expected result type of the /// call. /// /// \param numOptionalUnwraps The number of unwraps required to get the /// underlying function from the overload choice. /// /// \returns \c true if an error was encountered, \c false otherwise. bool simplifyAppliedOverloadsImpl(Constraint *disjunction, TypeVariableType *fnTypeVar, FunctionType *argFnType, unsigned numOptionalUnwraps, ConstraintLocatorBuilder locator); public: /// Attempt to simplify the set of overloads corresponding to a given /// bind overload disjunction. /// /// \param disjunction The disjunction for the set of overloads. /// /// \returns \c true if an error was encountered, \c false otherwise. bool simplifyAppliedOverloads(Constraint *disjunction, ConstraintLocatorBuilder locator); /// Attempt to simplify the set of overloads corresponding to a given /// function application constraint. /// /// \param fnType The type that describes the set of overloads for the /// function. /// /// \param argFnType The call signature, which includes the call arguments /// (as the function parameters) and the expected result type of the /// call. /// /// \returns \c true if an error was encountered, \c false otherwise. bool simplifyAppliedOverloads(Type fnType, FunctionType *argFnType, ConstraintLocatorBuilder locator); /// Retrieve the type that will be used when matching the given overload. Type getEffectiveOverloadType(ConstraintLocator *locator, const OverloadChoice &overload, DeclContext *useDC); /// Add a new overload set to the list of unresolved overload /// sets. void addOverloadSet(Type boundType, ArrayRef choices, DeclContext *useDC, ConstraintLocator *locator); void addOverloadSet(ArrayRef choices, ConstraintLocator *locator); /// Retrieve the allocator used by this constraint system. llvm::BumpPtrAllocator &getAllocator() { return Allocator; } template ArrayRef::value_type> allocateCopy(It start, It end) { using T = typename std::iterator_traits::value_type; T *result = (T*)getAllocator().Allocate(sizeof(T)*(end-start), alignof(T)); unsigned i; for (i = 0; start != end; ++start, ++i) new (result+i) T(*start); return ArrayRef(result, i); } template ArrayRef allocateCopy(ArrayRef array) { return allocateCopy(array.begin(), array.end()); } template ArrayRef allocateCopy(SmallVectorImpl &vec) { return allocateCopy(vec.begin(), vec.end()); } /// Generate constraints for the given solution target. /// /// \returns true if an error occurred, false otherwise. [[nodiscard]] bool generateConstraints(SyntacticElementTarget &target, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); /// Generate constraints for the body of the given tap expression. /// /// \param tap the tap expression /// /// \returns \c true if constraint generation failed, \c false otherwise [[nodiscard]] bool generateConstraints(TapExpr *tap); /// Generate constraints for the body of the given function or closure. /// /// \param fn The function or closure expression /// \param body The body of the given function that should be /// used for constraint generation. /// /// \returns \c true if constraint generation failed, \c false otherwise [[nodiscard]] bool generateConstraints(AnyFunctionRef fn, BraceStmt *body); /// Generate constraints for a given SingleValueStmtExpr. /// /// \returns \c true if constraint generation failed, \c false otherwise bool generateConstraints(SingleValueStmtExpr *E); /// Generate constraints for an array of ExprPatterns, forming a conjunction /// that solves each expression in turn. void generateConstraints(ArrayRef exprPatterns, ConstraintLocatorBuilder locator); /// Generate constraints for the given (unchecked) expression. /// /// \returns a possibly-sanitized expression, or null if an error occurred. [[nodiscard]] Expr *generateConstraints(Expr *E, DeclContext *dc); /// Generate constraints for binding the given pattern to the /// value of the given expression. /// /// \returns a possibly-sanitized initializer, or null if an error occurred. [[nodiscard]] Type generateConstraints(Pattern *P, ConstraintLocatorBuilder locator, bool bindPatternVarsOneWay, PatternBindingDecl *patternBinding, unsigned patternIndex); /// Generate constraints for a statement condition. /// /// \returns true if there was an error in constraint generation, false /// if generation succeeded. [[nodiscard]] bool generateConstraints(StmtCondition condition, DeclContext *dc); /// Generate constraints for a given set of overload choices. /// /// \param constraints The container of generated constraint choices. /// /// \param type The type each choice should be bound to. /// /// \param choices The set of choices to convert into bind overload /// constraints so solver could attempt each one. /// /// \param useDC The declaration context where each choice is used. /// /// \param locator The locator to use when generating constraints. /// /// \param requiresFix Determines whether choices require a fix to /// be included in the result. If the fix couldn't be provided by /// `getFix` for any given choice, such choice would be filtered out. /// /// \param getFix Optional callback to determine a fix for a given /// choice (first argument is a position of current choice, /// second - the choice in question). void generateOverloadConstraints( SmallVectorImpl &constraints, Type type, ArrayRef choices, DeclContext *useDC, ConstraintLocator *locator, bool requiresFix = false, llvm::function_ref getFix = [](unsigned, const OverloadChoice &) { return nullptr; }); /// Generate constraints for the given property that has an /// attached property wrapper. /// /// \param wrappedVar The property that has a property wrapper. /// \param initializerType The type of the initializer for the /// backing storage variable. /// \param propertyType The type of the wrapped property. /// /// \returns true if there is an error. [[nodiscard]] bool generateWrappedPropertyTypeConstraints(VarDecl *wrappedVar, Type initializerType, Type propertyType); /// Propagate constraints in an effort to enforce local /// consistency to reduce the time to solve the system. /// /// \returns true if the system is known to be inconsistent (have no /// solutions). bool propagateConstraints(); /// The result of attempting to resolve a constraint or set of /// constraints. enum class SolutionKind : char { /// The constraint has been solved completely, and provides no /// more information. Solved, /// The constraint could not be solved at this point. Unsolved, /// The constraint uncovers an inconsistency in the system. Error }; class TypeMatchResult { SolutionKind Kind; public: inline bool isSuccess() const { return Kind == SolutionKind::Solved; } inline bool isFailure() const { return Kind == SolutionKind::Error; } inline bool isAmbiguous() const { return Kind == SolutionKind::Unsolved; } static TypeMatchResult success() { return {SolutionKind::Solved}; } static TypeMatchResult failure() { return {SolutionKind::Error}; } static TypeMatchResult ambiguous() { return {SolutionKind::Unsolved}; } operator SolutionKind() { return Kind; } private: TypeMatchResult(SolutionKind result) : Kind(result) {} }; /// Attempt to repair typing failures and record fixes if needed. /// \return true if at least some of the failures has been repaired /// successfully, which allows type matcher to continue. bool repairFailures(Type lhs, Type rhs, ConstraintKind matchKind, TypeMatchOptions flags, SmallVectorImpl &conversionsOrFixes, ConstraintLocatorBuilder locator); TypeMatchResult matchPackTypes(PackType *pack1, PackType *pack2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); TypeMatchResult matchPackExpansionTypes(PackExpansionType *expansion1, PackExpansionType *expansion2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes(), which matches up two tuple types. /// /// \returns the result of performing the tuple-to-tuple conversion. TypeMatchResult matchTupleTypes(TupleType *tuple1, TupleType *tuple2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Match the @Sendable bit between two functions. TypeMatchResult matchFunctionSendability(FunctionType *func1, FunctionType *func2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes(), which matches up two function /// types. TypeMatchResult matchFunctionTypes(FunctionType *func1, FunctionType *func2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes() bool matchFunctionIsolations(FunctionType *func1, FunctionType *func2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes() bool matchFunctionLifetimes(const LifetimeDependentInterface &func1, const LifetimeDependentInterface &func2, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes(), which matches up a value to a /// superclass. TypeMatchResult matchSuperclassTypes(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes(), which matches up two types that /// refer to the same declaration via their generic arguments. TypeMatchResult matchDeepEqualityTypes(Type type1, Type type2, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes(), which matches up a value to an /// existential type. /// /// \param kind Either ConstraintKind::Subtype or ConstraintKind::ConformsTo. /// Usually this uses Subtype, but when matching the instance type of a /// metatype with the instance type of an existential metatype, since we /// want an actual conformance check. TypeMatchResult matchExistentialTypes(Type type1, Type type2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Subroutine of \c matchTypes(), used to bind a type to a /// type variable. TypeMatchResult matchTypesBindTypeVar( TypeVariableType *typeVar, Type type, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator, llvm::function_ref formUnsolvedResult); /// Matches two function result types for a function application. This is /// usually a bind, but also handles e.g IUO unwraps. TypeMatchResult matchFunctionResultTypes(Type expectedResult, Type fnResult, TypeMatchOptions flags, ConstraintLocatorBuilder locator); enum ImpliedResultConversionKind : unsigned { /// Usual subtyping rules apply. None, /// Accept Never subtype $T only. FromNever, /// Accept Never subtype $T, and $T subtype Void. ToVoid }; /// Determines if special subtyping rules for implied result contexts apply. /// /// We allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for /// closure literals and expressions representing an implied result of /// closures and if/switch expressions. ImpliedResultConversionKind getImpliedResultConversionKind(ConstraintLocator *locator); public: // FIXME: public due to statics in CSSimplify.cpp /// Attempt to match up types \c type1 and \c type2, which in effect /// is solving the given type constraint between these two types. /// /// \param type1 The first type, which is on the left of the type relation. /// /// \param type2 The second type, which is on the right of the type relation. /// /// \param kind The kind of type match being performed, e.g., exact match, /// trivial subtyping, subtyping, or conversion. /// /// \param flags A set of flags composed from the TMF_* constants, which /// indicates how the constraint should be simplified. /// /// \param locator The locator that will be used to track the location of /// the specific types being matched. /// /// \returns the result of attempting to solve this constraint. TypeMatchResult matchTypes(Type type1, Type type2, ConstraintKind kind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); TypeMatchResult getTypeMatchSuccess() { return TypeMatchResult::success(); } TypeMatchResult getTypeMatchFailure(ConstraintLocatorBuilder locator) { return TypeMatchResult::failure(); } TypeMatchResult getTypeMatchAmbiguous() { return TypeMatchResult::ambiguous(); } public: // Build a disjunction that attempts both T? and T for a particular // type binding. The choice of T? is preferred, and we will not // attempt T if we can type check with T? void buildDisjunctionForOptionalVsUnderlying(Type boundTy, Type ty, ConstraintLocator *locator); // Build a disjunction for types declared IUO. void buildDisjunctionForImplicitlyUnwrappedOptional(Type boundTy, Type type, ConstraintLocator *locator) { auto *disjunctionLocator = getConstraintLocator( locator, ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice); buildDisjunctionForOptionalVsUnderlying(boundTy, type, disjunctionLocator); } // Build a disjunction for dynamic lookup results, which are // implicitly unwrapped if needed. void buildDisjunctionForDynamicLookupResult(Type boundTy, Type type, ConstraintLocator *locator) { auto *dynamicLocator = getConstraintLocator(locator, ConstraintLocator::DynamicLookupResult); buildDisjunctionForOptionalVsUnderlying(boundTy, type, dynamicLocator); } void recordResolvedOverload(ConstraintLocator *locator, SelectedOverload choice); /// Build and allocate a prepared overload in the solver arena. PreparedOverload *prepareOverload(OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator, bool forDiagnostics); /// Populate the prepared overload with all type variables and constraints /// that are to be introduced into the constraint system when this choice /// is taken. /// /// Returns a pair consisting of the opened type, and the thrown error type. /// /// FIXME: As a transitional mechanism, if preparedOverload is nullptr, this /// immediately performs all operations. std::pair prepareOverloadImpl(OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator, PreparedOverloadBuilder *preparedOverload); void replayChanges( ConstraintLocator *locator, PreparedOverload *preparedOverload); /// Resolve the given overload set to the given choice. void resolveOverload(OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator, Type boundType, PreparedOverload *preparedOverload); /// Simplify a type, by replacing type variables with either their /// fixed types (if available) or their representatives. /// /// The resulting types can be compared canonically, so long as additional /// type equivalence requirements aren't introduced between comparisons. Type simplifyType(Type type); /// Simplify a type, by replacing type variables with either their /// fixed types (if available) or their representatives. /// /// \param flags If the simplified type has changed, this will be updated /// to include \c TMF_GenerateConstraints. /// /// The resulting types can be compared canonically, so long as additional /// type equivalence requirements aren't introduced between comparisons. Type simplifyType(Type type, TypeMatchOptions &flags) { Type result = simplifyType(type); if (result.getPointer() != type.getPointer()) flags |= TMF_GenerateConstraints; return result; } /// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint, /// perform a lookup into the specified base type to find a candidate list. /// The list returned includes the viable candidates as well as the unviable /// ones (along with reasons why they aren't viable). /// /// If includeInaccessibleMembers is set to true, this burns compile time to /// try to identify and classify inaccessible members that may be being /// referenced. MemberLookupResult performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, Type baseTy, FunctionRefInfo functionRefInfo, ConstraintLocator *memberLocator, bool includeInaccessibleMembers); /// Build implicit autoclosure expression wrapping a given expression. /// Given expression represents computed result of the closure. /// /// The \p ClosureDC must be the deepest possible context that /// contains this autoclosure expression. For example, /// /// func foo() { /// _ = { $0 || $1 || $2 } /// } /// /// Even though the decl context of $1 (after solution application) is /// `||`'s autoclosure parameter, we cannot know this until solution /// application has finished because autoclosure expressions are expanded in /// depth-first order then \c ContextualizeClosures comes around to clean up. /// All that is required is that the explicit closure be the context since it /// is the innermost context that can introduce potential new capturable /// declarations. Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType, DeclContext *ClosureDC, bool isDefaultWrappedValue = false, bool isAsyncLetWrapper = false); /// Builds a type-erased return expression that can be used in dynamic /// replacement. /// /// An expression needs type erasure if: /// 1. The expression is a return value. /// 2. The enclosing function is dynamic, a dynamic replacement, or /// `-enable-experimental-opaque-type-erasure` is used. /// 3. The enclosing function returns an opaque type. /// 4. The opaque type conforms to (exactly) one protocol, and the protocol /// has a declared type eraser. /// /// \returns the transformed return expression, or the original expression if /// no type erasure is needed. Expr *buildTypeErasedExpr(Expr *expr, DeclContext *dc, Type contextualType, ContextualTypePurpose purpose); /// Ensures that the given argument type conforms to the `Hashable` protocol /// and adds a conformance constraint if it does not. This is required for /// arguments used as key path components, as they serve as lookup keys. void verifyThatArgumentIsHashable(unsigned index, Type argType, ConstraintLocator *locator, SourceLoc loc); private: /// Determines whether or not a given conversion at a given locator requires /// the creation of a temporary value that's only valid for a limited scope. /// Such ephemeral conversions, such as array-to-pointer, cannot be passed to /// non-ephemeral parameters. ConversionEphemeralness isConversionEphemeral(ConversionRestrictionKind conversion, ConstraintLocatorBuilder locator); /// Simplifies a type by replacing type variables with the result of /// \c getFixedTypeFn and performing lookup on dependent member types. Type simplifyTypeImpl(Type type, llvm::function_ref getFixedTypeFn); /// Attempt to simplify the given construction constraint. /// /// \param valueType The type being constructed. /// /// \param fnType The argument type that will be the input to the /// valueType initializer and the result type will be the result of /// calling that initializer. /// /// \param flags A set of flags composed from the TMF_* constants, which /// indicates how the constraint should be simplified. /// /// \param locator Locator describing where this construction /// occurred. SolutionKind simplifyConstructionConstraint(Type valueType, FunctionType *fnType, TypeMatchOptions flags, DeclContext *DC, FunctionRefInfo functionRefInfo, ConstraintLocator *locator); /// Attempt to simplify the given superclass constraint. /// /// \param type The type being tested. /// \param classType The class type which the type should be a subclass of. /// \param locator Locator describing where this constraint occurred. SolutionKind simplifySubclassOfConstraint(Type type, Type classType, ConstraintLocatorBuilder locator, TypeMatchOptions flags); /// Attempt to simplify the given conformance constraint. /// /// \param type The type being tested. /// \param protocol The protocol to which the type should conform. /// \param kind Either ConstraintKind::Subtype or /// ConstraintKind::ConformsTo. /// \param locator Locator describing where this constraint occurred. SolutionKind simplifyConformsToConstraint(Type type, ProtocolDecl *protocol, ConstraintKind kind, ConstraintLocatorBuilder locator, TypeMatchOptions flags); void recordSynthesizedConformance(ConstraintLocator *locator, ProtocolDecl *conformance); /// Attempt to simplify the given conformance constraint. /// /// \param type The type being tested. /// \param protocol The protocol or protocol composition type to which the /// type should conform. /// \param locator Locator describing where this constraint occurred. /// /// \param kind If this is SelfTypeOfProtocol, we allow an existential type /// that contains the protocol but does not conform to it (eg, due to /// associated types). SolutionKind simplifyConformsToConstraint(Type type, Type protocol, ConstraintKind kind, ConstraintLocatorBuilder locator, TypeMatchOptions flags); /// Similar to \c simplifyConformsToConstraint but also checks for /// optional and pointer derived a given type. SolutionKind simplifyTransitivelyConformsTo(Type type, Type protocol, ConstraintLocatorBuilder locator, TypeMatchOptions flags); /// Attempt to simplify a checked-cast constraint. SolutionKind simplifyCheckedCastConstraint(Type fromType, Type toType, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given member constraint. SolutionKind simplifyMemberConstraint( ConstraintKind kind, Type baseType, DeclNameRef member, Type memberType, DeclContext *useDC, FunctionRefInfo functionRefInfo, ArrayRef outerAlternatives, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Lookup a dependent member, returning a null Type and recording a fix on /// failure. Type lookupDependentMember(Type base, AssociatedTypeDecl *assocTy, bool openExistential, ConstraintLocatorBuilder locator); /// Attempt to simplify the ForEachElement constraint. SolutionKind simplifyForEachElementConstraint(Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the optional object constraint. SolutionKind simplifyOptionalObjectConstraint( Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the BridgingConversion constraint. SolutionKind simplifyBridgingConstraint(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify a BindTupleOfFunctionParams constraint. SolutionKind simplifyBindTupleOfFunctionParamsConstraint(Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to match a pack element type with the fully resolved pattern type /// for the pack expansion. SolutionKind matchPackElementType(Type elementType, Type patternType, ConstraintLocatorBuilder locator); /// Attempt to simplify a PackElementOf constraint. /// /// Solving this constraint is delayed until the element type is fully /// resolved with no type variables. The element type is then mapped out /// of the opened element context and into the context of the surrounding /// function, effecively substituting opened element archetypes with their /// corresponding pack archetypes, and bound to the second type. SolutionKind simplifyPackElementOfConstraint(Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the ApplicableFunction constraint. SolutionKind simplifyApplicableFnConstraint( FunctionType *appliedFn, Type calleeTy, std::optional trailingClosureMatching, DeclContext *useDC, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the DynamicCallableApplicableFunction constraint. SolutionKind simplifyDynamicCallableApplicableFnConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given DynamicTypeOf constraint. SolutionKind simplifyDynamicTypeOfConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given EscapableFunctionOf constraint. SolutionKind simplifyEscapableFunctionOfConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given OpenedExistentialOf constraint. SolutionKind simplifyOpenedExistentialOfConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given KeyPathApplication constraint. SolutionKind simplifyKeyPathApplicationConstraint( Type keyPath, Type root, Type value, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given KeyPath constraint. SolutionKind simplifyKeyPathConstraint( Type keyPath, Type root, Type value, ArrayRef componentTypeVars, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given defaultable constraint. SolutionKind simplifyDefaultableConstraint(Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify the given fallback type constraint. SolutionKind simplifyFallbackTypeConstraint(Type defaultableType, Type fallbackType, ArrayRef referencedVars, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify a property wrapper constraint. SolutionKind simplifyPropertyWrapperConstraint(Type wrapperType, Type wrappedValueType, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Attempt to simplify a one-way constraint. SolutionKind simplifyOneWayConstraint(ConstraintKind kind, Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify an equality constraint between result and base types of /// an unresolved member chain. SolutionKind simplifyUnresolvedMemberChainBaseConstraint( Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify a conversion constraint by applying the given /// reduction rule, which is known to apply at the outermost level. SolutionKind simplifyRestrictedConstraintImpl( ConversionRestrictionKind restriction, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify a conversion constraint by applying the given /// reduction rule, which is known to apply at the outermost level. SolutionKind simplifyRestrictedConstraint( ConversionRestrictionKind restriction, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify a syntactic element constraint by generating required /// constraints to represent the given element in constraint system. SolutionKind simplifySyntacticElementConstraint( ASTNode element, ContextualTypeInfo context, bool isDiscarded, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify a shape constraint by binding the left-hand side to the /// reduced shape of the right-hand side. SolutionKind simplifyShapeOfConstraint( Type shapeTy, Type packTy, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify an explicit generic argument constraint by equating the /// opened generic types of the bound left-hand type variable to the /// pack type on the right-hand side. SolutionKind simplifyExplicitGenericArgumentsConstraint( Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify a same-shape constraint by comparing the reduced shape of the /// left hand side to the right hand side. SolutionKind simplifySameShapeConstraint(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Remove the tuple wrapping of left-hand type if it contains only a single /// unlabeled element that is a pack expansion. SolutionKind simplifyMaterializePackExpansionConstraint(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Extract the object type of the l-value type (type1) and bind it to /// to type2. SolutionKind simplifyLValueObjectConstraint(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); public: // FIXME: Public for use by static functions. /// Simplify a conversion constraint with a fix applied to it. SolutionKind simplifyFixConstraint(ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind, TypeMatchOptions flags, ConstraintLocatorBuilder locator); /// Simplify a conversion between Swift and C pointers. SolutionKind simplifyPointerToCPointerRestriction(Type type1, Type type2, TypeMatchOptions flags, ConstraintLocatorBuilder locator); public: /// Simplify the system of constraints, by breaking down complex /// constraints into simpler constraints. /// /// The result of simplification is a constraint system consisting of /// only simple constraints relating type variables to each other or /// directly to fixed types. There are no constraints that involve /// type constructors on both sides. The simplified constraint system may, /// of course, include type variables for which we have constraints but /// no fixed type. Such type variables are left to the solver to bind. /// /// \returns true if an error occurred, false otherwise. bool simplify(); /// Simplify the given constraint. SolutionKind simplifyConstraint(const Constraint &constraint); /// Simplify the given disjunction choice. void simplifyDisjunctionChoice(Constraint *choice); /// Apply the given result builder to the closure expression. /// /// \note builderType must be a contexutal type - callers should /// open the builder type or map it into context as appropriate. /// /// \returns \c None when the result builder cannot be applied at all, /// otherwise the result of applying the result builder. std::optional matchResultBuilder(AnyFunctionRef fn, Type builderType, Type bodyResultType, ConstraintKind bodyResultConstraintKind, Type contextualType, ConstraintLocatorBuilder locator); /// Used by matchResultBuilder() to update resultBuilderTransformed and record /// a change in the trail. void recordResultBuilderTransform(AnyFunctionRef fn, AppliedBuilderTransform transformInfo); /// Undo the above change. void removeResultBuilderTransform(AnyFunctionRef fn); /// Matches a wrapped or projected value parameter type to its backing /// property wrapper type by applying the property wrapper. TypeMatchResult applyPropertyWrapperToParameter( Type wrapperType, Type paramType, ParamDecl *param, Identifier argLabel, ConstraintKind matchKind, ConstraintLocator *locator, ConstraintLocator *calleeLocator, PreparedOverloadBuilder *preparedOverload = nullptr); /// Used by applyPropertyWrapperToParameter() to update appliedPropertyWrappers /// and record a change in the trail. void applyPropertyWrapper(Expr *anchor, AppliedPropertyWrapper applied, PreparedOverloadBuilder *preparedOverload = nullptr); /// Undo the above change. void removePropertyWrapper(Expr *anchor); /// Determine whether given type variable with its set of bindings is viable /// to be attempted on the next step of the solver. const inference::BindingSet *determineBestBindings(); /// Get bindings for the given type variable based on current /// state of the constraint system. /// /// FIXME: Remove this. inference::BindingSet getBindingsFor(TypeVariableType *typeVar); private: /// Add a constraint to the constraint system. SolutionKind addConstraintImpl(ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator, bool isFavored); /// Adds a constraint for the conversion of an argument to a parameter. Do not /// call directly, use \c addConstraint instead. SolutionKind addArgumentConversionConstraintImpl(ConstraintKind kind, Type first, Type second, ConstraintLocatorBuilder locator); /// Record a particular disjunction choice and add a change to the trail. void recordDisjunctionChoice(ConstraintLocator *locator, unsigned index); /// Record applied disjunction and add a change to the trail. void recordAppliedDisjunction(ConstraintLocator *locator, FunctionType *type); /// Get the applicable function constraint for a disjunction, if there is one. Constraint *getApplicableFnConstraint(Constraint *disjunction); /// Filter the set of disjunction terms, keeping only those where the /// predicate returns \c true. /// /// The terms of the disjunction that are filtered out will be marked as /// "disabled" so they won't be visited later. If only one term remains /// enabled, the disjunction itself will be returned and that term will /// be made active. /// /// \param restoreOnFail If true, then all of the disabled terms will /// be re-enabled when this function returns \c Error. /// /// \returns One of \c Solved (only a single term remained), /// \c Unsolved (more than one disjunction terms remain), or /// \c Error (all terms were filtered out). SolutionKind filterDisjunction(Constraint *disjunction, bool restoreOnFail, llvm::function_ref pred); bool isReadOnlyKeyPathComponent(const AbstractStorageDecl *storage, SourceLoc referenceLoc); public: /// If the given argument, specified by its type and expression, a reference /// to a generic function? bool isArgumentGenericFunction(Type argType, Expr *argExpr); // Given a type variable, attempt to find the disjunction of // bind overloads associated with it. This may return null in cases where // the disjunction has either not been created or binds the type variable // in some manner other than by binding overloads. /// /// \param numOptionalUnwraps If non-null, this will receive the number /// of "optional object of" constraints that this function looked through /// to uncover the disjunction. The actual overloads will have this number /// of optionals wrapping the type. Constraint *getUnboundBindOverloadDisjunction( TypeVariableType *tyvar, unsigned *numOptionalUnwraps = nullptr); SolverDisjunction &getRemainingDisjunction(Constraint *disjunction); private: /// Solve the system of constraints after it has already been /// simplified. /// /// \param solutions The set of solutions to this system of constraints. /// /// \returns true if an error occurred, false otherwise. bool solveSimplified(SmallVectorImpl &solutions); /// Pick a disjunction from the InactiveConstraints list. /// /// \returns The selected disjunction and a set of it's favored choices. std::optional>> selectDisjunction(); /// Pick a conjunction from the InactiveConstraints list. /// /// \returns The selected conjunction. Constraint *selectConjunction(); void diagnoseTooComplex(SourceLoc fallbackLoc, SolutionResult &result); /// Solve the system of constraints generated from provided expression. /// /// \param target The target to generate constraints from. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. SolutionResult solveImpl(SyntacticElementTarget &target, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); public: /// Pre-check the target, validating any types that occur in it /// and folding sequence expressions. static bool preCheckTarget(SyntacticElementTarget &target); /// Solve the system of constraints generated from provided target. /// /// \param target The target that we'll generate constraints from, which /// may be updated by the solving process. /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// /// \returns the set of solutions, if any were found, or \c None if an /// error occurred. When \c None, an error has been emitted. std::optional> solve(SyntacticElementTarget &target, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); /// Solve the system of constraints. /// /// \param solutions The set of solutions to this system of constraints. /// /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// /// \returns true if an error occurred, false otherwise. Note that multiple /// ambiguous solutions for the same constraint system are considered to be /// success by this API. bool solve(SmallVectorImpl &solutions, FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); /// Solve the system of constraints. /// /// \param allowFreeTypeVariables How to bind free type variables in /// the solution. /// /// \param allowFixes Whether to allow fixes in the solution. /// /// \returns a solution if a single unambiguous one could be found, or None if /// ambiguous or unsolvable. std::optional solveSingle(FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow, bool allowFixes = false); /// Assuming that constraints have already been generated, solve the /// constraint system for code completion, writing all solutions to /// \p solutions. /// /// This method is designed to be used for code completion which means that /// it doesn't mutate given expression, even if there is a single valid /// solution, and constraint solver is allowed to produce partially correct /// solutions. Such solutions can have any number of holes in them. /// /// \param solutions The solutions produced for the given target without /// filtering. void solveForCodeCompletion(SmallVectorImpl &solutions); /// Generate constraints for \p target and solve the resulting constraint /// system for code completion (see overload above). /// /// \returns `false` if this call fails (e.g. pre-check or constraint /// generation fails), `true` otherwise. bool solveForCodeCompletion(SyntacticElementTarget &target, SmallVectorImpl &solutions); private: /// Solve the system of constraints. /// /// This method responsible for running search/solver algorithm. /// It doesn't filter solutions, that's the job of top-level `solve` methods. /// /// \param solutions The set of solutions to this system of constraints. void solveImpl(SmallVectorImpl &solutions); /// Compare two solutions to the same set of constraints. /// /// \param cs The constraint system. /// \param solutions All of the solutions to the system. /// \param diff The differences among the solutions. /// \param idx1 The index of the first solution. /// \param idx2 The index of the second solution. static SolutionCompareResult compareSolutions(ConstraintSystem &cs, ArrayRef solutions, const SolutionDiff &diff, unsigned idx1, unsigned idx2); void startExpressionTimer(); public: /// Increase the score of the given kind for the current (partial) solution /// along the current solver path. void increaseScore(ScoreKind kind, ConstraintLocatorBuilder Locator, unsigned value = 1); /// Primitive form of the above. Records a change in the trail. void increaseScore(ScoreKind kind, unsigned value); /// Apply the score from a partial solution. Records the change in the /// trail. void replayScore(const Score &score); /// Temporarily zero out the score, and record this in the trail so that /// we restore the score when the scope ends. Used when solving a /// ConjunctionStep. void clearScore(); /// Determine whether this solution is guaranteed to be worse than the best /// solution found so far. bool worseThanBestSolution() const; /// Given a set of viable solutions, find the best /// solution. /// /// \param solutions The set of viable solutions to consider. /// /// \param minimize If true, then in the case where there is no single /// best solution, minimize the set of solutions by removing any solutions /// that are identical to or worse than some other solution. This operation /// is quadratic. /// /// \returns The index of the best solution, or nothing if there was no /// best solution. std::optional findBestSolution(SmallVectorImpl &solutions, bool minimize); /// Apply a given solution to the target, producing a fully /// type-checked target or \c None if an error occurred. /// /// \param target the target to which the solution will be applied. std::optional applySolution(Solution &solution, SyntacticElementTarget target); /// Apply the given solution to the given statement-condition. std::optional applySolution(Solution &solution, StmtCondition condition, DeclContext *dc); /// Apply the given solution to the given function's body and, for /// closure expressions, the expression itself. /// /// \param fn The function to which the solution is being applied. /// \param rewriter The rewriter to apply the solution with. /// bool applySolution(AnyFunctionRef fn, SyntacticElementTargetRewriter &rewriter); /// Apply the given solution to the given closure body. /// /// \param fn The function or closure to which the solution is being applied. /// \param rewriter The rewriter to apply the solution with. /// /// \returns true if solution cannot be applied. bool applySolutionToBody(AnyFunctionRef fn, SyntacticElementTargetRewriter &rewriter); /// Apply the given solution to the given SingleValueStmtExpr. /// /// \param SVE The SingleValueStmtExpr to rewrite. /// \param rewriter The rewriter to apply the solution with. /// /// \returns true if solution cannot be applied. bool applySolutionToSingleValueStmt(SingleValueStmtExpr *SVE, SyntacticElementTargetRewriter &rewriter); /// Apply the given solution to the given tap expression. /// /// \param tapExpr The tap expression to which the solution is being applied. /// \param rewriter The rewriter to apply the solution with. /// /// \returns true if solution cannot be applied. bool applySolutionToBody(TapExpr *tapExpr, SyntacticElementTargetRewriter &rewriter); /// Set the current sub-expression (of a multi-statement closure, etc) for /// the purposes of diagnosing "reasonable time" errors. void startExpression(ASTNode node); /// The source range of the target being type checked. SourceRange getCurrentSourceRange() const { return CurrentRange; } /// Return the number of solver scopes created so far. unsigned getNumSolverScopes() const { return NumSolverScopes; } /// Return the number of solver trail steps taken so far. unsigned getNumTrailSteps() const { return NumTrailSteps; } /// Determine if we've already explored too many paths in an /// attempt to solve this expression. std::pair isAlreadyTooComplex = {false, SourceRange()}; /// If optional is not nil, result is guaranteed to point at a valid /// location. std::optional getTooComplexRange() const { auto range = isAlreadyTooComplex.second; return range.isValid() ? range : std::optional(); } bool isTooComplex(size_t solutionMemory); bool isTooComplex(ArrayRef solutions) { if (isAlreadyTooComplex.first) return true; size_t solutionMemory = 0; for (auto const& s : solutions) { solutionMemory += s.getTotalMemory(); } return isTooComplex(solutionMemory); } // If the given constraint is an applied disjunction, get the argument function // that the disjunction is applied to. FunctionType *getAppliedDisjunctionArgumentFunction(const Constraint *disjunction) { assert(disjunction->getKind() == ConstraintKind::Disjunction); auto found = AppliedDisjunctions.find(disjunction->getLocator()); if (found == AppliedDisjunctions.end()) return nullptr; return found->second; } /// The overload sets that have already been resolved along the current path. const llvm::DenseMap & getResolvedOverloads() const { return ResolvedOverloads; } /// If we aren't certain that we've emitted a diagnostic, emit a fallback /// diagnostic. void maybeProduceFallbackDiagnostic(SourceLoc loc) const; /// Check whether given AST node represents an argument of an application /// of some sort (call, operator invocation, subscript etc.) /// and returns a locator for the argument application. E.g. for regular /// calls `test(42)` passing `42` should return a locator with the entire call /// as the anchor, and a path to the argument at index `0`. ConstraintLocator *getArgumentLocator(Expr *expr); /// Determine whether given locator represents an argument to declaration /// imported from C/ObjectiveC. bool isArgumentOfImportedDecl(ConstraintLocatorBuilder locator); /// Visit each subexpression that will be part of the constraint system /// of the given expression, including those in closure bodies that will be /// part of the constraint system. void forEachExpr(Expr *expr, llvm::function_ref callback); /// Attempts to infer a capability of a key path (i.e. whether it /// is read-only, writable, etc.) based on the referenced members. /// /// \param keyPath The key path literal expression. /// /// \returns `bool` to indicate whether key path is valid or not, /// and capability if it could be determined. std::pair> inferKeyPathLiteralCapability(KeyPathExpr *keyPath); /// A convenience overload of \c inferKeyPathLiteralCapability. /// /// \param keyPathType The type variable that represents the key path literal. /// /// \returns `bool` to indicate whether key path is valid or not, /// and capability if it could be determined. std::pair> inferKeyPathLiteralCapability(TypeVariableType *keyPathType); SWIFT_DEBUG_DUMP; SWIFT_DEBUG_DUMPER(dump(Expr *)); void print(raw_ostream &out) const; void print(raw_ostream &out, Expr *) const; }; /// A function object suitable for use as an \c OpenRequirementFn that "opens" /// the requirements for a given type's generic signature given a set of /// argument substitutions. class OpenGenericTypeRequirements { ConstraintSystem &cs; const ConstraintLocatorBuilder &locator; PreparedOverloadBuilder *preparedOverload; public: explicit OpenGenericTypeRequirements( ConstraintSystem &cs, const ConstraintLocatorBuilder &locator, PreparedOverloadBuilder *preparedOverload) : cs(cs), locator(locator), preparedOverload(preparedOverload) {} void operator()(GenericTypeDecl *decl, TypeSubstitutionFn subst) const; }; /// Compute the shuffle required to map from a given tuple type to /// another tuple type. /// /// \param fromTuple The tuple type we're converting from. /// /// \param toTuple The tuple type we're converting to. /// /// \param sources Will be populated with information about the source of each /// of the elements for the result tuple. The indices into this array are the /// indices of the tuple type we're converting to, while the values are /// an index into the source tuple. /// /// \returns true if no tuple conversion is possible, false otherwise. bool computeTupleShuffle(TupleType *fromTuple, TupleType *toTuple, SmallVectorImpl &sources); /// Class used as the base for listeners to the \c matchCallArguments process. /// /// By default, none of the callbacks do anything. class MatchCallArgumentListener { public: virtual ~MatchCallArgumentListener(); /// Indicates that the argument at the given index does not match any /// parameter. /// /// \param argIdx The index of the extra argument. /// /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool extraArgument(unsigned argIdx); /// Indicates that no argument was provided for the parameter at the given /// indices. /// /// \param paramIdx The index of the parameter that is missing an argument. /// \param argInsertIdx The index in the argument list where this argument was /// expected. virtual std::optional missingArgument(unsigned paramIdx, unsigned argInsertIdx); /// Indicate that there was no label given when one was expected by parameter. /// /// \param paramIndex The index of the parameter that is missing a label. /// /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool missingLabel(unsigned paramIndex); /// Indicate that there was label given when none was expected by parameter. /// /// \param paramIndex The index of the parameter that wasn't expecting a label. /// /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool extraneousLabel(unsigned paramIndex); /// Indicate that there was a label given with a typo(s) in it. /// /// \param paramIndex The index of the parameter with misspelled label. /// /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool incorrectLabel(unsigned paramIndex); /// Indicates that an argument is out-of-order with respect to a previously- /// seen argument. /// /// \param argIdx The argument that came too late in the argument list. /// \param prevArgIdx The argument that the \c argIdx should have preceded. /// /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool outOfOrderArgument( unsigned argIdx, unsigned prevArgIdx, ArrayRef bindings); /// Indicates that the arguments need to be relabeled to match the parameters. /// /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool relabelArguments(ArrayRef newNames); /// \returns true if matchCallArguments should try to claim the argument at /// \p argIndex while recovering from a failure. This is used to prevent /// claiming of arguments after the code completion token. virtual bool shouldClaimArgDuringRecovery(unsigned argIdx); /// \returns true if \p arg can be claimed even though its argument label /// doesn't match. This is the case for arguments representing the code /// completion token if they don't contain a label. In these cases completion /// will suggest the label. virtual bool canClaimArgIgnoringNameMismatch(const AnyFunctionType::Param &arg); }; /// For a callsite containing a code completion expression, stores the index of /// the arg containing it along with the index of the first trailing closure and /// how many arguments were passed in total. struct CompletionArgInfo { unsigned completionIdx; std::optional firstTrailingIdx; unsigned argCount; /// \returns true if the given argument index is possibly about to be written /// by the user (given the completion index) so shouldn't be penalised as /// missing when ranking solutions. bool allowsMissingArgAt(unsigned argInsertIdx, AnyFunctionType::Param param); /// \returns true if the argument containing the completion location is before /// the argument with the given index. bool isBefore(unsigned argIdx) { return completionIdx < argIdx; } }; /// Extracts the index of the argument containing the code completion location /// from the provided anchor if it's a \c CallExpr, \c SubscriptExpr, or /// \c ObjectLiteralExpr. std::optional getCompletionArgInfo(ASTNode anchor, ConstraintSystem &cs); /// Match the call arguments (as described by the given argument type) to /// the parameters (as described by the given parameter type). /// /// \param args The arguments. /// \param params The parameters. /// \param paramInfo Declaration-level information about the parameters. /// \param unlabeledTrailingClosureIndex The index of an unlabeled trailing closure, /// if any. /// \param allowFixes Whether to allow fixes when matching arguments. /// /// \param listener Listener that will be notified when certain problems occur, /// e.g., to produce a diagnostic. /// /// \param trailingClosureMatching If specified, the trailing closure matching /// direction to use. Otherwise, the matching direction will be determined /// based on language mode. /// /// \returns the bindings produced by performing this matching, or \c None if /// the match failed. std::optional matchCallArguments( SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, std::optional unlabeledTrailingClosureIndex, bool allowFixes, MatchCallArgumentListener &listener, std::optional trailingClosureMatching); /// Given an expression that is the target of argument labels (for a call, /// subscript, etc.), find the underlying target expression. Expr *getArgumentLabelTargetExpr(Expr *fn); /// Returns true if a reference to a member on a given base type will apply /// its curried self parameter, assuming it has one. /// /// This is true for most member references, however isn't true for things /// like an instance member being referenced on a metatype, where the /// curried self parameter remains unapplied. bool doesMemberRefApplyCurriedSelf(Type baseTy, const ValueDecl *decl); /// Simplify the given locator by zeroing in on the most specific /// subexpression described by the locator. /// /// This routine can also find the corresponding "target" locator, which /// typically provides the other end of a relational constraint. For example, /// if the primary locator refers to a function argument, the target locator /// will be set to refer to the corresponding function parameter. /// /// \param cs The constraint system in which the locator will be simplified. /// /// \param locator The locator to simplify. /// /// \param range Will be populated with an "interesting" range. /// /// \return the simplified locator. ConstraintLocator *simplifyLocator(ConstraintSystem &cs, ConstraintLocator *locator, SourceRange &range); void simplifyLocator(ASTNode &anchor, ArrayRef &path, SourceRange &range); /// Simplify the given locator down to a specific anchor expression, /// if possible. /// /// \returns the anchor expression if it fully describes the locator, or /// null otherwise. ASTNode simplifyLocatorToAnchor(ConstraintLocator *locator); /// Retrieve argument at specified index from given node. /// The expression could be "application", "subscript" or "member" call. /// /// \returns argument expression or `nullptr` if given "base" expression /// wasn't of one of the kinds listed above. Expr *getArgumentExpr(ASTNode node, unsigned index); /// Determine whether given locator points to one of the arguments /// associated with the call to an operator. If the operator name /// is empty `true` is returned for any kind of operator. bool isOperatorArgument(ConstraintLocator *locator, StringRef expectedOperator = ""); /// Determine whether given locator points to one of the arguments /// associated with implicit `~=` (pattern-matching) operator bool isArgumentOfPatternMatchingOperator(ConstraintLocator *locator); /// Determine whether given locator points to one of the arguments /// associated with `===` and `!==` operators. bool isArgumentOfReferenceEqualityOperator(ConstraintLocator *locator); /// Determine whether the given AST node is a reference to a /// pattern-matching operator `~=` bool isPatternMatchingOperator(ASTNode node); /// Determine whether the given AST node is a reference to a /// "standard" comparison operator such as "==", "!=", ">" etc. bool isStandardComparisonOperator(ASTNode node); /// If given expression references operator overload(s) /// extract and produce name of the operator. std::optional getOperatorName(Expr *expr); // Check whether argument of the call at given position refers to // parameter marked as `@autoclosure`. This function is used to // maintain source compatibility with Swift versions < 5, // previously examples like following used to type-check: // // func foo(_ x: @autoclosure () -> Int) {} // func bar(_ y: @autoclosure () -> Int) { // foo(y) // } bool isAutoClosureArgument(Expr *argExpr); /// Checks whether referencing the given overload choice results in the self /// parameter being applied, meaning that it's dropped from the type of the /// reference. bool hasAppliedSelf(ConstraintSystem &cs, const OverloadChoice &choice); bool hasAppliedSelf(const Solution &S, const OverloadChoice &choice); bool hasAppliedSelf(const OverloadChoice &choice, llvm::function_ref getFixedType); /// Check whether given type conforms to `RawRepresentable` protocol /// and return witness type. Type isRawRepresentable(ConstraintSystem &cs, Type type); /// Compute the type that shall stand in for dynamic 'Self' in a member /// reference with a base of the given object type. /// /// \param memberLocator The locator of the member constraint; used to retrieve /// the expression that the locator is anchored to. Type getDynamicSelfReplacementType(Type baseObjTy, const ValueDecl *member, ConstraintLocator *memberLocator); ValueDecl *getOverloadChoiceDecl(Constraint *choice); /// Determine whether this type is considered `Sendable` when captured /// i.e. a base type of a partially applied member reference. /// /// The requirement here is more strict than regular `Sendable` conformance: /// - The type has to conform to `Sendable`; /// - All referenced type parameters have to conform to `SendableMetatype`. /// /// This function requires the type to be fully resolved. bool isSendableCapture(Type type); /// Find any references to external type variables used in the body of a /// conjunction element (e.g closures, taps, if/switch expressions). /// /// This includes: /// - Not yet resolved outer VarDecls (including closure parameters) /// - Outer pack expansions that are not yet fully resolved /// - Return statements with a contextual type that has not yet been resolved /// /// This is required because isolated conjunctions, just like single-expression /// closures, have to be connected to type variables they are going to use, /// otherwise they'll get placed in a separate solver component and would never /// produce a solution. class TypeVarRefCollector : public ASTWalker { ConstraintSystem &CS; DeclContext *DC; ConstraintLocator *Locator; llvm::SmallSetVector TypeVars; unsigned DCDepth = 0; public: TypeVarRefCollector(ConstraintSystem &cs, DeclContext *dc, ConstraintLocator *locator) : CS(cs), DC(dc), Locator(locator) {} /// Infer the referenced type variables from a given decl. void inferTypeVars(Decl *D); void inferTypeVars(PackExpansionExpr *); MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Arguments; } PreWalkResult walkToExprPre(Expr *expr) override; PostWalkResult walkToExprPost(Expr *expr) override; PreWalkResult walkToStmtPre(Stmt *stmt) override; PreWalkAction walkToDeclPre(Decl *D) override { // We only need to walk into PatternBindingDecls, other kinds of decls // cannot reference outer vars. return Action::VisitNodeIf(isa(D)); } ArrayRef getTypeVars() const { return TypeVars.getArrayRef(); } }; /// Determine whether the given type is a PartialKeyPath and /// AnyKeyPath or existential type thererof, for example, /// `PartialKeyPath<...> & Sendable`. bool isTypeErasedKeyPathType(Type type); /// Determine whether given declaration is one for a key path /// `{Writable, ReferenceWritable}KeyPath`. bool isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl); /// Determine whether given closure has any `return` /// statements that could produce non-void result. bool hasResultExpr(ClosureExpr *closure); /// Emit diagnostics for syntactic restrictions within a given solution /// application target. void performSyntacticDiagnosticsForTarget(const SyntacticElementTarget &target, bool isExprStmt); /// Given a member of a protocol, check whether `Self` type of that /// protocol is contextually bound to some concrete type via same-type /// generic requirement and if so return that type or null type otherwise. Type getConcreteReplacementForProtocolSelfType(ValueDecl *member); /// Determine whether given disjunction constraint represents a set /// of operator overload choices. bool isOperatorDisjunction(Constraint *disjunction); /// Find out whether closure body has any `async` or `await` expressions, /// declarations, or statements directly in its body (no in other closures /// or nested declarations). ASTNode findAsyncNode(ClosureExpr *closure); /// Check whether the given binding represents a placeholder variable that /// has to get its type inferred at a first use site. /// /// \returns The currently assigned type if it's a placeholder, /// empty type otherwise. Type isPlaceholderVar(PatternBindingDecl *PB); /// Dump an anchor node for a constraint locator or contextual type. void dumpAnchor(ASTNode anchor, SourceManager *SM, raw_ostream &out); bool isPackExpansionType(Type type); /// Check whether the type is a tuple consisting of a single unlabeled element /// of \c PackExpansionType or a type variable that represents a pack expansion /// type. bool isSingleUnlabeledPackExpansionTuple(Type type); bool containsPackExpansionType(ArrayRef params); bool containsPackExpansionType(TupleType *tuple); /// \returns null if \c type is not a single unlabeled pack expansion tuple. Type getPatternTypeOfSingleUnlabeledPackExpansionTuple(Type type); /// Check whether this is a reference to one of the special result builder /// methods prefixed with `build*` i.e. `buildBlock`, `buildExpression` etc. bool isResultBuilderMethodReference(ASTContext &, UnresolvedDotExpr *); /// Determine the number of applications applied for a given FunctionRefInfo. unsigned getNumApplications(bool hasAppliedSelf, FunctionRefInfo functionRefInfo); /// Determine whether the debug output is enabled for the given target. bool debugConstraintSolverForTarget(ASTContext &C, SyntacticElementTarget target); /// Determine whether the given declaration is only generic because it /// adopted typed throws. bool isGenericOnlyOverThrownType(AbstractFunctionDecl *func); } // end namespace constraints /// If the expression has the effect of a forced downcast, find the /// underlying forced downcast expression. ForcedCheckedCastExpr *findForcedDowncast(ASTContext &ctx, Expr *expr); /// Assuming the expression appears in a consuming context, /// if it does not already have an explicit `consume`, /// can I add `consume` around this expression? /// /// \param module represents the module in which the expr appears bool canAddExplicitConsume(constraints::Solution &sol, ModuleDecl *module, Expr *expr); // Return true if, when replacing "" with " ?? T", parentheses need // to be added around first in order to maintain the correct precedence. bool exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC, Expr *expr); // Return true if, when replacing "" with " as T", parentheses need // to be added around the new expression in order to maintain the correct // precedence. bool exprNeedsParensAfterAddingNilCoalescing( DeclContext *DC, Expr *expr, llvm::function_ref getParent); /// Return true if, when replacing "" with " op ", /// parentheses must be added around "" to allow the new operator /// to bind correctly. bool exprNeedsParensInsideFollowingOperator(DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG); /// Return true if, when replacing "" with " op ", /// parentheses must be added around the new operator to prevent it from binding /// incorrectly in the surrounding context. bool exprNeedsParensOutsideFollowingOperator( DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG, llvm::function_ref getParent); /// Determine whether this is a SIMD operator. bool isSIMDOperator(ValueDecl *value); std::string describeGenericType(ValueDecl *GP, bool includeName = false); } // end namespace swift namespace llvm { template <> struct DenseMapInfo { using Key = swift::constraints::SyntacticElementTargetKey; static inline Key getEmptyKey() { return Key(Key::Kind::empty); } static inline Key getTombstoneKey() { return Key(Key::Kind::tombstone); } static inline unsigned getHashValue(Key key) { return key.getHashValue(); } static bool isEqual(Key a, Key b) { return a == b; } }; } // end namespace llvm #endif // SWIFT_SEMA_CONSTRAINT_SYSTEM_H