//===--- 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/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 using namespace swift::constraints::inference; 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; } // 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 /// Allocate memory within the given constraint system. void *operator new(size_t bytes, swift::constraints::ConstraintSystem& cs, size_t alignment = 8); 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); }; /// Describes the algorithm to use for trailing closure matching. enum class TrailingClosureMatching { /// Match a trailing closure to the first parameter that appears to work. Forward, /// Match a trailing closure to the last parameter. Backward, }; 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 ExpressionTimer { public: using AnchorType = llvm::PointerUnion; private: AnchorType Anchor; ASTContext &Context; llvm::TimeRecord StartTime; /// The number of seconds from creation until /// this timer is considered expired. unsigned ThresholdInSecs; bool PrintDebugTiming; bool PrintWarning; public: const static unsigned NoLimit = (unsigned) -1; ExpressionTimer(AnchorType Anchor, ConstraintSystem &CS, unsigned thresholdInSecs); ~ExpressionTimer(); AnchorType getAnchor() const { return Anchor; } SourceRange getAffectedRange() const; unsigned getWarnLimit() const { return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking; } 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 /// Options that describe how a type variable can be used. enum TypeVariableOptions { /// Whether the type variable can be bound to an lvalue type or not. TVO_CanBindToLValue = 0x01, /// Whether the type variable can be bound to an inout type or not. TVO_CanBindToInOut = 0x02, /// Whether the type variable can be bound to a non-escaping type or not. TVO_CanBindToNoEscape = 0x04, /// Whether the type variable can be bound to a hole or not. TVO_CanBindToHole = 0x08, /// Whether a more specific deduction for this type variable implies a /// better solution to the constraint system. TVO_PrefersSubtypeBinding = 0x10, /// Whether the type variable can be bound to a pack type or not. TVO_CanBindToPack = 0x20, /// Whether the type variable can be bound only to a pack expansion type. TVO_PackExpansion = 0x40, }; enum class KeyPathMutability : uint8_t { ReadOnly, Writable, ReferenceWritable }; using KeyPathCapability = std::pair; /// The implementation object for a type variable used within the /// constraint-solving type checker. /// /// The implementation object for a type variable contains information about /// the type variable, where it was generated, what protocols it must conform /// to, what specific types it might be and, eventually, the fixed type to /// which it is assigned. class TypeVariableType::Implementation { /// The locator that describes where this type variable was generated. constraints::ConstraintLocator *locator; /// Either the parent of this type variable within an equivalence /// class of type variables, or the fixed type to which this type variable /// type is bound. llvm::PointerUnion ParentOrFixed; /// The corresponding node in the constraint graph. constraints::ConstraintGraphNode *GraphNode = nullptr; /// Temporary state for ConstraintGraph::computeConnectedComponents(), /// stored inline for performance. llvm::PointerIntPair Component; friend class constraints::SolverTrail; public: /// Retrieve the type variable associated with this implementation. TypeVariableType *getTypeVariable() const { return reinterpret_cast( const_cast(this)) - 1; } explicit Implementation(constraints::ConstraintLocator *locator, unsigned options) : locator(locator), ParentOrFixed(getTypeVariable()) { getTypeVariable()->Bits.TypeVariableType.Options = options; } /// Retrieve the unique ID corresponding to this type variable. unsigned getID() const { return getTypeVariable()->getID(); } unsigned getRawOptions() const { return getTypeVariable()->Bits.TypeVariableType.Options; } void setRawOptions(unsigned bits) { getTypeVariable()->Bits.TypeVariableType.Options = bits; assert(getTypeVariable()->Bits.TypeVariableType.Options == bits && "Truncation"); } /// Whether this type variable can bind to an LValueType. bool canBindToLValue() const { return getRawOptions() & TVO_CanBindToLValue; } /// Whether this type variable can bind to an InOutType. bool canBindToInOut() const { return getRawOptions() & TVO_CanBindToInOut; } /// Whether this type variable can bind to a noescape FunctionType. bool canBindToNoEscape() const { return getRawOptions() & TVO_CanBindToNoEscape; } /// Whether this type variable can bind to a PlaceholderType. bool canBindToHole() const { return getRawOptions() & TVO_CanBindToHole; } /// Whether this type variable can bind to a PackType. bool canBindToPack() const { return getRawOptions() & TVO_CanBindToPack; } /// Whether this type variable can bind only to PackExpansionType. bool isPackExpansion() const { return getRawOptions() & TVO_PackExpansion; } /// Whether this type variable prefers a subtype binding over a supertype /// binding. bool prefersSubtypeBinding() const { return getRawOptions() & TVO_PrefersSubtypeBinding; } /// Retrieve the corresponding node in the constraint graph. constraints::ConstraintGraphNode *getGraphNode() const { return GraphNode; } /// Set the corresponding node in the constraint graph. void setGraphNode(constraints::ConstraintGraphNode *newNode) { GraphNode = newNode; } /// Check whether this type variable either has a representative that /// is not itself or has a fixed type binding. bool hasRepresentativeOrFixed() const { // If we have a fixed type, we're done. if (!ParentOrFixed.is()) return true; // Check whether the representative is different from our own type // variable. return ParentOrFixed.get() != getTypeVariable(); } /// Low-level accessor; use getRepresentative() or getFixedType() instead. llvm::PointerUnion getRepresentativeOrFixed() const { return ParentOrFixed; } /// Record the current type-variable binding. void recordBinding(constraints::SolverTrail &trail) { trail.recordChange(constraints::SolverTrail::Change::UpdatedTypeVariable( getTypeVariable(), ParentOrFixed, getRawOptions())); } /// Retrieve the locator describing where this type variable was /// created. constraints::ConstraintLocator *getLocator() const { return locator; } /// Retrieve the generic parameter opened by this type variable. GenericTypeParamType *getGenericParameter() const; /// Returns the \c ExprKind of this type variable if it's the type of an /// atomic literal expression, meaning the literal can't be composed of subexpressions. /// Otherwise, returns \c None. std::optional getAtomicLiteralKind() const; /// Determine whether this type variable represents a closure type. bool isClosureType() const; /// Determine whether this type variable represents a type of tap expression. bool isTapType() const; /// Determine whether this type variable represents one of the /// parameter types associated with a closure. bool isClosureParameterType() const; /// Determine whether this type variable represents a closure result type. bool isClosureResultType() const; /// Determine whether this type variable represents /// a type of a key path expression. bool isKeyPathType() const; /// Determine whether this type variable represents a root type of a key path /// expression. bool isKeyPathRoot() const; /// Determine whether this type variable represents a value type of a key path /// expression. bool isKeyPathValue() const; /// Determine whether this type variable represents an index parameter of /// a special `subscript(keyPath:)` subscript. bool isKeyPathSubscriptIndex() const; /// Determine whether this type variable represents a subscript result type. bool isSubscriptResultType() const; /// Determine whether this type variable represents an opened /// type parameter pack. bool isParameterPack() const; /// Determine whether this type variable represents a code completion /// expression. bool isCodeCompletionToken() const; /// Determine whether this type variable represents an opened opaque type. bool isOpaqueType() const; /// Determine whether this type variable represents a type of a collection /// literal (represented by `ArrayExpr` and `DictionaryExpr` in AST). bool isCollectionLiteralType() const; /// Determine whether this type variable represents a literal such /// as an integer value, a floating-point value with and without a sign. bool isNumberLiteralType() const; /// Determine whether this type variable represents a result type of a /// function call. bool isFunctionResult() const; /// Determine whether this type variable represents a type of the ternary /// operator. bool isTernary() const; /// Retrieve the representative of the equivalence class to which this /// type variable belongs. /// /// \param trail The record of changes made by retrieving the representative, /// which can happen due to path compression. If null, path compression is /// not performed. TypeVariableType * getRepresentative(constraints::SolverTrail *trail) { // Find the representative type variable. auto result = getTypeVariable(); Implementation *impl = this; while (impl->ParentOrFixed.is()) { // Extract the representative. auto nextTV = impl->ParentOrFixed.get(); if (nextTV == result) break; result = nextTV; impl = &nextTV->getImpl(); } if (impl == this || !trail || trail->isUndoActive()) return result; // Perform path compression. impl = this; while (impl->ParentOrFixed.is()) { // Extract the representative. auto nextTV = impl->ParentOrFixed.get(); if (nextTV == result) break; // Record the state change. impl->recordBinding(*trail); impl->ParentOrFixed = result; impl = &nextTV->getImpl(); } return result; } /// Merge the equivalence class of this type variable with the /// equivalence class of another type variable. /// /// \param other The type variable to merge with. /// /// \param trail The record of state changes. void mergeEquivalenceClasses(TypeVariableType *other, constraints::SolverTrail *trail) { ASSERT(getID() < other->getImpl().getID()); auto otherRep = other->getImpl().getRepresentative(trail); if (trail) otherRep->getImpl().recordBinding(*trail); otherRep->getImpl().ParentOrFixed = getTypeVariable(); if (canBindToLValue() && !otherRep->getImpl().canBindToLValue()) { if (trail) recordBinding(*trail); getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToLValue; } if (canBindToInOut() && !otherRep->getImpl().canBindToInOut()) { if (trail) recordBinding(*trail); getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToInOut; } if (canBindToNoEscape() && !otherRep->getImpl().canBindToNoEscape()) { if (trail) recordBinding(*trail); getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToNoEscape; } if (canBindToPack() && !otherRep->getImpl().canBindToPack()) { if (trail) recordBinding(*trail); getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToPack; } } /// Retrieve the fixed type that corresponds to this type variable, /// if there is one. /// /// \returns the fixed type associated with this type variable, or a null /// type if there is no fixed type. /// /// \param trail The record of changes made by retrieving the representative, /// which can happen due to path compression. If null, path compression is /// not performed. Type getFixedType(constraints::SolverTrail *trail) { // Find the representative type variable. auto rep = getRepresentative(trail); Implementation &repImpl = rep->getImpl(); // Return the bound type if there is one, otherwise, null. return repImpl.ParentOrFixed.dyn_cast(); } /// Assign a fixed type to this equivalence class. void assignFixedType(Type type, constraints::SolverTrail *trail) { assert((!getFixedType(nullptr) || getFixedType(nullptr)->isEqual(type)) && "Already has a fixed type!"); auto rep = getRepresentative(trail); if (trail) rep->getImpl().recordBinding(*trail); rep->getImpl().ParentOrFixed = type.getPointer(); } void setCanBindToLValue(constraints::SolverTrail *trail, bool enabled) { auto &impl = getRepresentative(trail)->getImpl(); if (trail) impl.recordBinding(*trail); if (enabled) impl.getTypeVariable()->Bits.TypeVariableType.Options |= TVO_CanBindToLValue; else impl.getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToLValue; } void setCanBindToNoEscape(constraints::SolverTrail *trail, bool enabled) { auto &impl = getRepresentative(trail)->getImpl(); if (trail) impl.recordBinding(*trail); if (enabled) impl.getTypeVariable()->Bits.TypeVariableType.Options |= TVO_CanBindToNoEscape; else impl.getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToNoEscape; } void enableCanBindToHole(constraints::SolverTrail *trail) { auto &impl = getRepresentative(trail)->getImpl(); if (trail) impl.recordBinding(*trail); impl.getTypeVariable()->Bits.TypeVariableType.Options |= TVO_CanBindToHole; } void setComponent(TypeVariableType *parent) { Component.setPointerAndInt(parent, /*valid=*/false); } TypeVariableType *getComponent() const { auto *rep = getTypeVariable(); while (rep != rep->getImpl().Component.getPointer()) rep = rep->getImpl().Component.getPointer(); // Path compression if (rep != getTypeVariable()) { const_cast(this) ->Component.setPointer(rep); } return rep; } bool isValidComponent() const { ASSERT(Component.getPointer() == getTypeVariable()); return Component.getInt(); } bool markValidComponent() { if (Component.getInt()) return false; ASSERT(Component.getPointer() == getTypeVariable()); Component.setInt(1); return true; } /// Print the type variable to the given output stream. void print(llvm::raw_ostream &OS); private: StringRef getTypeVariableOptions(TypeVariableOptions TVO) const { #define ENTRY(Kind, String) case TypeVariableOptions::Kind: return String switch (TVO) { ENTRY(TVO_CanBindToLValue, "lvalue"); ENTRY(TVO_CanBindToInOut, "inout"); ENTRY(TVO_CanBindToNoEscape, "noescape"); ENTRY(TVO_CanBindToHole, "hole"); ENTRY(TVO_PrefersSubtypeBinding, ""); ENTRY(TVO_CanBindToPack, "pack"); ENTRY(TVO_PackExpansion, "pack expansion"); } #undef ENTRY } }; namespace constraints { template T *castToExpr(ASTNode node) { return cast(node.get()); } 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() || !node.is()) return false; auto *E = node.get(); 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(node.get()); } 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 }; /// An overload that has been selected in a particular solution. /// /// A selected overload captures the specific overload choice (e.g., a /// particular declaration) as well as the type to which the reference to the /// declaration was opened, which may involve type variables. struct SelectedOverload { /// The overload choice. const OverloadChoice choice; /// The opened type of the base of the reference to this overload, if /// we're referencing a member. const Type openedFullType; /// The opened type of the base of the reference to this overload, adjusted /// for `@preconcurrency` or other contextual type-altering attributes. const Type adjustedOpenedFullType; /// The opened type produced by referring to this overload. const Type openedType; /// The opened type produced by referring to this overload, adjusted for /// `@preconcurrency` or other contextual type-altering attributes. const Type adjustedOpenedType; /// The type that this overload binds. Note that this may differ from /// adjustedOpenedType, for example it will include any IUO unwrapping that has taken /// place. const Type boundType; }; /// Provides information about the application of a function argument to a /// parameter. class FunctionArgApplyInfo { ArgumentList *ArgList; Expr *ArgExpr; unsigned ArgIdx; Type ArgType; unsigned ParamIdx; Type FnInterfaceType; FunctionType *FnType; const ValueDecl *Callee; FunctionArgApplyInfo(ArgumentList *argList, Expr *argExpr, unsigned argIdx, Type argType, unsigned paramIdx, Type fnInterfaceType, FunctionType *fnType, const ValueDecl *callee) : ArgList(argList), ArgExpr(argExpr), ArgIdx(argIdx), ArgType(argType), ParamIdx(paramIdx), FnInterfaceType(fnInterfaceType), FnType(fnType), Callee(callee) {} public: static std::optional get(ArgumentList *argList, Expr *argExpr, unsigned argIdx, Type argType, unsigned paramIdx, Type fnInterfaceType, FunctionType *fnType, const ValueDecl *callee) { assert(fnType); if (argIdx >= argList->size()) return std::nullopt; if (paramIdx >= fnType->getNumParams()) return std::nullopt; return FunctionArgApplyInfo(argList, argExpr, argIdx, argType, paramIdx, fnInterfaceType, fnType, callee); } /// \returns The list of the arguments used for this application. ArgumentList *getArgList() const { return ArgList; } /// \returns The argument being applied. Expr *getArgExpr() const { return ArgExpr; } /// \returns The position of the argument, starting at 1. unsigned getArgPosition() const { return ArgIdx + 1; } /// \returns The position of the parameter, starting at 1. unsigned getParamPosition() const { return ParamIdx + 1; } /// \returns The type of the argument being applied, including any generic /// substitutions. /// /// \param withSpecifier Whether to keep the inout or @lvalue specifier of /// the argument, if any. Type getArgType(bool withSpecifier = false) const { return withSpecifier ? ArgType : ArgType->getWithoutSpecifierType(); } /// \returns The label for the argument being applied. Identifier getArgLabel() const { return ArgList->getLabel(ArgIdx); } Identifier getParamLabel() const { auto param = FnType->getParams()[ParamIdx]; return param.getLabel(); } /// \returns A textual description of the argument suitable for diagnostics. /// For an argument with an unambiguous label, this will the label. Otherwise /// it will be its position in the argument list. StringRef getArgDescription(SmallVectorImpl &scratch) const { llvm::raw_svector_ostream stream(scratch); // Use the argument label only if it's unique within the argument list. auto argLabel = getArgLabel(); auto useArgLabel = [&]() -> bool { if (argLabel.empty()) return false; SmallVector scratch; return llvm::count(ArgList->getArgumentLabels(scratch), argLabel) == 1; }; if (useArgLabel()) { stream << "'"; stream << argLabel; stream << "'"; } else { stream << "#"; stream << getArgPosition(); } return StringRef(scratch.data(), scratch.size()); } /// Whether the argument is a trailing closure. bool isTrailingClosure() const { return ArgList->isTrailingClosureIndex(ArgIdx); } /// \returns The interface type for the function being applied. Note that this /// may not a function type, for example it could be a generic parameter. Type getFnInterfaceType() const { return FnInterfaceType; } /// \returns The function type being applied, including any generic /// substitutions. FunctionType *getFnType() const { return FnType; } /// \returns The callee for the application. const ValueDecl *getCallee() const { return Callee; } private: Type getParamTypeImpl(AnyFunctionType *fnTy, bool lookThroughAutoclosure) const { auto param = fnTy->getParams()[ParamIdx]; auto paramTy = param.getPlainType(); if (lookThroughAutoclosure && param.isAutoClosure() && paramTy->is()) paramTy = paramTy->castTo()->getResult(); return paramTy; } public: /// \returns The type of the parameter which the argument is being applied to, /// including any generic substitutions. /// /// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter /// should be treated as being of type T. Type getParamType(bool lookThroughAutoclosure = true) const { return getParamTypeImpl(FnType, lookThroughAutoclosure); } /// \returns The interface type of the parameter which the argument is being /// applied to. /// /// \param lookThroughAutoclosure Whether an @autoclosure () -> T parameter /// should be treated as being of type T. Type getParamInterfaceType(bool lookThroughAutoclosure = true) const { auto interfaceFnTy = FnInterfaceType->getAs(); if (!interfaceFnTy) { // If the interface type isn't a function, then just return the resolved // parameter type. return getParamType(lookThroughAutoclosure)->mapTypeOutOfContext(); } return getParamTypeImpl(interfaceFnTy, lookThroughAutoclosure); } /// \returns The flags of the parameter which the argument is being applied /// to. ParameterTypeFlags getParameterFlags() const { return FnType->getParams()[ParamIdx].getParameterFlags(); } ParameterTypeFlags getParameterFlagsAtIndex(unsigned idx) const { return FnType->getParams()[idx].getParameterFlags(); } }; /// Describes an aspect of a solution that affects its overall score, i.e., a /// user-defined conversions. enum ScoreKind: unsigned int { // These values are used as indices into a Score value. /// A fix needs to be applied to the source. SK_Fix, /// A hole in the constraint system. SK_Hole, /// A reference to an @unavailable declaration. SK_Unavailable, /// A reference to a declaration from a module that has not been imported. SK_MissingImport, /// A reference to an async function in a synchronous context. /// /// \note Any score kind after this is considered a conversion that doesn't /// require fixing the source and will be ignored during code completion. SK_AsyncInSyncMismatch, /// Synchronous function in an asynchronous context or a conversion of /// a synchronous function to an asynchronous one. SK_SyncInAsync, /// A use of the "forward" scan for trailing closures. SK_ForwardTrailingClosure, /// A use of a disfavored overload. SK_DisfavoredOverload, /// A member for an \c UnresolvedMemberExpr found via unwrapped optional base. SK_UnresolvedMemberViaOptional, /// An implicit force of an implicitly unwrapped optional value. SK_ForceUnchecked, /// An implicit conversion from a value of one type (lhs) /// to another type (rhs) via implicit initialization of /// `rhs` type with an argument of `lhs` value. SK_ImplicitValueConversion, /// A user-defined conversion. SK_UserConversion, /// A non-trivial function conversion. SK_FunctionConversion, /// A literal expression bound to a non-default literal type. SK_NonDefaultLiteral, /// An implicit upcast conversion between collection types. SK_CollectionUpcastConversion, /// A value-to-optional conversion. SK_ValueToOptional, /// A conversion to an empty existential type ('Any' or '{}'). SK_EmptyExistentialConversion, /// A key path application subscript. SK_KeyPathSubscript, /// A conversion from a string, array, or inout to a pointer. SK_ValueToPointerConversion, /// A closure/function conversion to an autoclosure parameter. SK_FunctionToAutoClosureConversion, /// A type with a missing conformance(s) that has be synthesized /// or diagnosed later, such types are allowed to appear in /// a valid solution. SK_MissingSynthesizableConformance, /// An unapplied reference to a function. The purpose of this /// score bit is to prune overload choices that are functions /// when a solution has already been found using property. /// /// \Note The solver only prefers variables over functions /// to resolve ambiguities, so please be sure that any score /// kind added after this is truly less impactful. Only other /// ambiguity tie-breakers should go after this; anything else /// should be added above. SK_UnappliedFunction, SK_LastScoreKind = SK_UnappliedFunction, }; /// The number of score kinds. const unsigned NumScoreKinds = SK_LastScoreKind + 1; /// Describes what happened when a result builder transform was applied /// to a particular closure. struct AppliedBuilderTransform { /// The builder type that was applied to the closure. Type builderType; /// The result type of the body, to which the returned expression will be /// converted. Opaque types should be unopened. Type bodyResultType; /// If transform is applied to a closure, this type represents /// contextual type the closure is converted type (e.g. a parameter /// type or or pattern type). Type contextualType; /// The version of the original body with result builder applied /// as AST transformation. BraceStmt *transformedBody; }; struct Score; /// Display a score. llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score); /// Describes the fixed score of a solution to the constraint system. struct Score { unsigned Data[NumScoreKinds] = {}; friend Score &operator+=(Score &x, const Score &y) { for (unsigned i = 0; i != NumScoreKinds; ++i) { x.Data[i] += y.Data[i]; } return x; } friend Score operator+(const Score &x, const Score &y) { Score result; for (unsigned i = 0; i != NumScoreKinds; ++i) { result.Data[i] = x.Data[i] + y.Data[i]; } return result; } friend Score operator-(const Score &x, const Score &y) { Score result; for (unsigned i = 0; i != NumScoreKinds; ++i) { ASSERT(x.Data[i] >= y.Data[i]); result.Data[i] = x.Data[i] - y.Data[i]; } return result; } friend Score &operator-=(Score &x, const Score &y) { for (unsigned i = 0; i != NumScoreKinds; ++i) { ASSERT(x.Data[i] >= y.Data[i]); x.Data[i] -= y.Data[i]; } return x; } friend bool operator==(const Score &x, const Score &y) { for (unsigned i = 0; i != NumScoreKinds; ++i) { if (x.Data[i] != y.Data[i]) return false; } return true; } friend bool operator!=(const Score &x, const Score &y) { return !(x == y); } friend bool operator<(const Score &x, const Score &y) { for (unsigned i = 0; i != NumScoreKinds; ++i) { if (x.Data[i] < y.Data[i]) return true; if (x.Data[i] > y.Data[i]) return false; } return false; } friend bool operator<=(const Score &x, const Score &y) { return !(y < x); } friend bool operator>(const Score &x, const Score &y) { return y < x; } friend bool operator>=(const Score &x, const Score &y) { return !(x < y); } /// Return ScoreKind descriptions for printing alongside non-zero ScoreKinds /// in debug output. static std::string getNameFor(ScoreKind kind) { switch (kind) { case SK_Hole: return "hole"; case SK_Unavailable: return "use of an unavailable declaration"; case SK_MissingImport: return "use of a declaration that has not been imported"; case SK_AsyncInSyncMismatch: return "async-in-synchronous mismatch"; case SK_SyncInAsync: return "sync-in-asynchronous"; case SK_ForwardTrailingClosure: return "forward scan when matching a trailing closure"; case SK_Fix: return "applied fix"; case SK_DisfavoredOverload: return "disfavored overload"; case SK_UnresolvedMemberViaOptional: return "unwrapping optional at unresolved member base"; case SK_ForceUnchecked: return "force of an implicitly unwrapped optional"; case SK_UserConversion: return "user conversion"; case SK_FunctionConversion: return "function conversion"; case SK_NonDefaultLiteral: return "non-default literal"; case SK_CollectionUpcastConversion: return "collection upcast conversion"; case SK_ValueToOptional: return "value to optional promotion"; case SK_EmptyExistentialConversion: return "empty-existential conversion"; case SK_KeyPathSubscript: return "key path subscript"; case SK_ValueToPointerConversion: return "value-to-pointer conversion"; case SK_FunctionToAutoClosureConversion: return "function to autoclosure parameter conversion"; case SK_ImplicitValueConversion: return "value-to-value conversion"; case SK_UnappliedFunction: return "use of overloaded unapplied function"; case SK_MissingSynthesizableConformance: return "type with missing synthesizable conformance"; } } /// Print Score list a with brief description of any non-zero ScoreKinds. void print(llvm::raw_ostream &out) const { bool hasNonDefault = false; for (unsigned int i = 0; i < NumScoreKinds; ++i) { if (Data[i] != 0) { out << " ["; out << getNameFor(ScoreKind(i)); out << "(s), weight: "; out << std::to_string(NumScoreKinds - i); out << ", impact: "; out << std::to_string(Data[i]); out << "]"; hasNonDefault = true; } } if (!hasNonDefault) { out << " "; } } }; /// Describes a dependent type that has been opened to a particular type /// variable. using OpenedType = std::pair; /// Describes the information about a case label item that needs to be tracked /// within the constraint system. struct CaseLabelItemInfo { Pattern *pattern; Expr *guardExpr; }; /// 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 arguments to which a parameter binds. /// FIXME: This is an awful data structure. We want the equivalent of a /// TinyPtrVector for unsigned values. using ParamBinding = SmallVector; /// The result of calling matchCallArguments(). struct MatchCallArgumentResult { /// The direction of trailing closure matching that was performed. TrailingClosureMatching trailingClosureMatching; /// The parameter bindings determined by the match. SmallVector parameterBindings; /// When present, the forward and backward scans each produced a result, /// and the parameter bindings are different. The primary result will be /// forwarding, and this represents the backward binding. std::optional> backwardParameterBindings; friend bool operator==(const MatchCallArgumentResult &lhs, const MatchCallArgumentResult &rhs) { if (lhs.trailingClosureMatching != rhs.trailingClosureMatching) return false; if (lhs.parameterBindings != rhs.parameterBindings) return false; return lhs.backwardParameterBindings == rhs.backwardParameterBindings; } /// Generate a result that maps the provided number of arguments to the same /// number of parameters via forward match. static MatchCallArgumentResult forArity(unsigned argCount) { SmallVector Bindings; for (unsigned i : range(argCount)) Bindings.push_back({i}); return {TrailingClosureMatching::Forward, Bindings, std::nullopt}; } }; /// Describes a potential throw site in the constraint system. /// /// For example, given `try f() + a[b] + x.y`, each of `f()`, `a[b]`, `x`, and /// `x.y` is a potential throw site. struct PotentialThrowSite { enum Kind { /// The application of a function or subscript. Application, /// An explicit 'throw'. ExplicitThrow, /// A non-exhaustive do...catch, which rethrows whatever is thrown from /// inside it's `do` block. NonExhaustiveDoCatch, /// A property access that can throw an error. PropertyAccess, } kind; /// The type that describes the potential throw site, such as the type of the /// function being called or type being thrown. Type type; /// The locator that specifies where the throwing operation occurs. ConstraintLocator *locator; }; enum class ImpliedResultKind { /// A regular implied result, this applies to e.g single expression bodies of /// function decls, and implied 'then' statements outside of closures. Regular, /// An implied result for a closure, e.g a single expression body. ForClosure }; /// A complete solution to a constraint system. /// /// A solution to a constraint system consists of type variable bindings to /// concrete types for every type variable that is used in the constraint /// system along with a set of mappings from each constraint locator /// involving an overload set to the selected overload. class Solution { /// The constraint system this solution solves. ConstraintSystem *constraintSystem; /// The fixed score for this solution. Score FixedScore; /// The total memory used by this solution. std::optional TotalMemory; public: /// Create a solution for the given constraint system. Solution(ConstraintSystem &cs, const Score &score) : constraintSystem(&cs), FixedScore(score) {} // Solution is a non-copyable type for performance reasons. Solution(const Solution &other) = delete; Solution &operator=(const Solution &other) = delete; Solution(Solution &&other) = default; Solution &operator=(Solution &&other) = default; size_t getTotalMemory() const; /// Retrieve the constraint system that this solution solves. ConstraintSystem &getConstraintSystem() const { return *constraintSystem; } DeclContext *getDC() const; /// The set of type bindings. llvm::MapVector typeBindings; /// The set of overload choices along with their types. llvm::DenseMap overloadChoices; /// The set of constraint restrictions used to arrive at this restriction, /// which informs constraint application. llvm::DenseMap, ConversionRestrictionKind> ConstraintRestrictions; /// The list of fixes that need to be applied to the initial expression /// to make the solution work. std::vector Fixes; /// The list of fixed requirements. using FixedRequirement = std::tuple; std::vector FixedRequirements; /// 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; /// For locators associated with call expressions, the trailing closure /// matching rule and parameter bindings that were applied. llvm::DenseMap argumentMatchingChoices; /// The set of disjunction choices used to arrive at this solution, /// which informs constraint application. llvm::DenseMap DisjunctionChoices; /// A map from applied disjunction constraints to the corresponding /// argument function type. llvm::DenseMap AppliedDisjunctions; /// The set of opened types for a given locator. llvm::DenseMap> OpenedTypes; /// The opened existential type for a given locator. llvm::DenseMap OpenedExistentialTypes; llvm::DenseMap OpenedPackExpansionTypes; /// The generic environment that can open pack elements for a given /// pack expansion. llvm::DenseMap PackExpansionEnvironments; /// The pack expansion expression for a given pack element. llvm::DenseMap PackElementExpansions; /// The locators of \c Defaultable constraints whose defaults were used. llvm::DenseSet DefaultedConstraints; /// The node -> type mappings introduced by this solution. llvm::DenseMap nodeTypes; /// The key path component types introduced by this solution. llvm::DenseMap, Type> keyPathComponentTypes; /// The key path expression and its root type, value type, and decl context /// introduced by this solution. llvm::DenseMap> KeyPaths; /// Contextual types introduced by this solution. std::vector> contextualTypes; /// Maps AST nodes to their target. llvm::DenseMap targets; /// Maps case label items to information tracked about them as they are /// being solved. llvm::DenseMap caseLabelItems; /// Maps catch nodes to the set of potential throw sites that will be caught /// at that location. /// Keep track of all of the potential throw sites. std::vector> potentialThrowSites; /// A map of expressions to the ExprPatterns that they are being solved as /// a part of. llvm::DenseMap exprPatterns; /// The set of parameters that have been inferred to be 'isolated'. llvm::DenseSet isolatedParams; /// The set of closures that have been inferred to be "isolated by /// preconcurrency". llvm::DenseSet preconcurrencyClosures; /// The set of functions that have been transformed by a result builder. llvm::MapVector resultBuilderTransformed; /// A map from argument expressions to their applied property wrapper expressions. llvm::DenseMap> appliedPropertyWrappers; ArrayRef getAppliedPropertyWrappers(ASTNode anchor) { auto found = appliedPropertyWrappers.find(anchor); if (found != appliedPropertyWrappers.end()) return found->second; return ArrayRef(); } /// 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; /// The set of implicitly generated `.callAsFunction` root expressions. llvm::DenseMap ImplicitCallAsFunctionRoots; /// The set of conformances synthesized during solving (i.e. for /// ad-hoc distributed `SerializationRequirement` conformances). llvm::DenseMap SynthesizedConformances; /// Record a new argument matching choice for given locator that maps a /// single argument to a single parameter. void recordSingleArgMatchingChoice(ConstraintLocator *locator); /// Simplify the given type by substituting all occurrences of /// type variables for their fixed types. /// /// \param wantInterfaceType If true, maps the resulting type out of context, /// and replaces type variables for opened generic parameters with the /// generic parameter types. Should only be used for diagnostic logic. Type simplifyType(Type type, bool wantInterfaceType = false) const; // To aid code completion, we need to attempt to convert type placeholders // back into underlying generic parameters if possible, since type // of the code completion expression is used as "expected" (or contextual) // type so it's helpful to know what requirements it has to filter // the list of possible member candidates e.g. // // \code // func test(_: [T]) {} // // test(42.#^MEMBERS^#) // \code // // It's impossible to resolve `T` in this case but code completion // expression should still have a type of `[T]` instead of `[<>]` // because it helps to produce correct contextual member list based on // a conformance requirement associated with generic parameter `T`. Type simplifyTypeForCodeCompletion(Type type) const; /// Coerce the given expression to the given type. /// /// This operation cannot fail. /// /// \param expr The expression to coerce. /// \param toType The type to coerce the expression to. /// \param locator Locator used to describe the location of this expression. /// /// \returns the coerced expression, which will have type \c ToType. Expr *coerceToType(Expr *expr, Type toType, ConstraintLocator *locator); /// Compute the set of substitutions for a generic signature opened at the /// given locator. /// /// \param decl The underlying declaration for which the substitutions are /// computed. /// /// \param sig The generic signature. /// /// \param locator The locator that describes where the substitutions came /// from. SubstitutionMap computeSubstitutions(NullablePtr decl, GenericSignature sig, ConstraintLocator *locator) const; /// Resolves the contextual substitutions for a reference to a declaration /// at a given locator. ConcreteDeclRef resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocator *locator) const; /// Return the disjunction choice for the given constraint location. unsigned getDisjunctionChoice(ConstraintLocator *locator) const { assert(DisjunctionChoices.count(locator)); return DisjunctionChoices.find(locator)->second; } /// 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(const Expr *E) const { auto result = ImpliedResults.find(E); if (result == ImpliedResults.end()) return std::nullopt; return result->second; } /// Retrieve the fixed score of this solution const Score &getFixedScore() const { return FixedScore; } /// Retrieve the fixed score of this solution Score &getFixedScore() { return FixedScore; } /// Check whether this solution has a fixed binding for the given type /// variable. bool hasFixedType(TypeVariableType *typeVar) const; /// Retrieve the fixed type for the given type variable. Type getFixedType(TypeVariableType *typeVar) const; /// Try to resolve the given locator to a declaration within this /// solution. Note that this only returns a decl for a direct reference such /// as \c x.foo and will not return a decl for \c x.foo(). ConcreteDeclRef resolveLocatorToDecl(ConstraintLocator *locator) const; /// Retrieve the overload choice associated with the given /// locator. SelectedOverload getOverloadChoice(ConstraintLocator *locator) const { return *getOverloadChoiceIfAvailable(locator); } /// Retrieve the overload choice for the callee associated with the given /// locator. SelectedOverload getCalleeOverloadChoice(ConstraintLocator *locator) const; /// Retrieve the overload choice associated with the given /// locator, if any. std::optional getOverloadChoiceIfAvailable(ConstraintLocator *locator) const { auto known = overloadChoices.find(locator); if (known != overloadChoices.end()) return known->second; return std::nullopt; } /// Retrieve the overload choice for the callee associated with the given /// locator, if any. std::optional getCalleeOverloadChoiceIfAvailable(ConstraintLocator *locator) const; std::optional getTargetFor(SyntacticElementTargetKey key) const; ConstraintLocator *getCalleeLocator(ConstraintLocator *locator, bool lookThroughApply = true) const; ConstraintLocator * getConstraintLocator(ASTNode anchor, ArrayRef path = {}) const; ConstraintLocator *getConstraintLocator(ConstraintLocator *baseLocator, ArrayRef path) const; void setExprTypes(Expr *expr) const; bool hasType(ASTNode node) const; /// Returns \c true if the \p ComponentIndex-th component in \p KP has a type /// associated with it. bool hasType(const KeyPathExpr *KP, unsigned ComponentIndex) const; /// Retrieve the type of the given node, as recorded in this solution. Type getType(ASTNode node) const; /// Retrieve the type of the \p ComponentIndex-th component in \p KP. Type getType(const KeyPathExpr *KP, unsigned ComponentIndex) const; TypeVariableType *getKeyPathRootType(const KeyPathExpr *keyPath) const; TypeVariableType * getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const; /// Retrieve the type of the given node as recorded in this solution /// and resolve all of the type variables in contains to form a fully /// "resolved" concrete type. Type getResolvedType(ASTNode node) const; std::optional getContextualTypeInfo(ASTNode anchor) const { for (const auto &entry : contextualTypes) { if (entry.first == anchor) return entry.second; } return std::nullopt; } Type getContextualType(ASTNode anchor) const { if (auto info = getContextualTypeInfo(anchor)) { // The contextual information record could contain the purpose // without a type i.e. when the context is an optional-some or // an invalid pattern binding. if (auto contextualTy = info->getType()) return simplifyType(contextualTy); } return Type(); } ContextualTypePurpose getContextualTypePurpose(ASTNode anchor) const { if (auto info = getContextualTypeInfo(anchor)) { return info->purpose; } return CTP_Unused; } /// Retrieve the generic environment for the opened element of a given pack /// expansion, or \c nullptr if no environment was recorded. GenericEnvironment * getPackExpansionEnvironment(PackExpansionExpr *expr) const; /// For a given locator describing a function argument conversion, or a /// constraint within an argument conversion, returns information about the /// application of the argument to its parameter. If the locator is not /// for an argument conversion, returns \c None. std::optional getFunctionArgApplyInfo(ConstraintLocator *) const; /// Retrieve the builder transform that was applied to this function, if any. const AppliedBuilderTransform *getAppliedBuilderTransform( AnyFunctionRef fn) const { auto known = resultBuilderTransformed.find(fn); return known != resultBuilderTransformed.end() ? &known->second : nullptr; } /// Retrieve the solved ExprPattern that corresponds to provided /// sub-expression. NullablePtr getExprPatternFor(Expr *E) const { auto result = exprPatterns.find(E); if (result == exprPatterns.end()) return nullptr; return result->second; } /// This method implements functionality of `Expr::isTypeReference` /// with data provided by a given solution. bool isTypeReference(Expr *E) const; /// Call Expr::isIsStaticallyDerivedMetatype on the given /// expression, using a custom accessor for the type on the /// expression that reads the type from the Solution /// expression type map. bool isStaticallyDerivedMetatype(Expr *E) const; /// Retrieve the argument list that is associated with a call at the given /// locator. ArgumentList *getArgumentList(ConstraintLocator *locator) const; std::optional getConversionRestriction(CanType type1, CanType type2) const; SWIFT_DEBUG_DUMP; /// Dump this solution. void dump(raw_ostream &OS, unsigned indent) 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, the client wants a best-effort solution to the constraint system, /// but can tolerate a solution where all of the constraints are solved, but /// not all type variables have been determined. In this case, the constraint /// system is not applied to the expression AST, but the ConstraintSystem is /// left in-tact. AllowUnresolvedTypeVariables = 0x04, /// 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 = 0x08, /// If set, we are solving specifically to determine the type of a /// CodeCompletionExpr, and should continue in the presence of errors wherever /// possible. ForCodeCompletion = 0x10, /// 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 = 0x20, /// When set, ignore async/sync mismatches IgnoreAsyncSyncMismatch = 0x40, /// Disable macro expansions. DisableMacroExpansions = 0x80, /// Enable old type-checker performance hacks. EnablePerformanceHacks = 0x100, }; /// 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; /// If there is a favored candidate in the viable list, this indicates its /// index. unsigned FavoredChoice = ~0U; /// 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, }; /// 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); } std::optional getFavoredIndex() const { return (FavoredChoice == ~0U) ? std::optional() : FavoredChoice; } }; /// 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; virtual std::optional rewriteTarget(SyntacticElementTarget target) = 0; virtual ~SyntacticElementTargetRewriter() = default; }; enum class ConstraintSystemPhase { ConstraintGeneration, Solving, Diagnostics, Finalization }; /// 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 { ASTContext &Context; 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; /// The total number of disjunctions created. unsigned CountDisjunctions = 0; 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; /// Current phase of the constraint system lifetime. ConstraintSystemPhase Phase = ConstraintSystemPhase::ConstraintGeneration; /// 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 all of the related constraint systems. 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; /// 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; /// 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 used as roots /// for some `.callAsFunction` calls. The key here is "base" locator for /// the `.callAsFunction` member reference. llvm::SmallDenseMap ImplicitCallAsFunctionRoots; /// The set of conformances synthesized during solving (i.e. for /// ad-hoc distributed `SerializationRequirement` conformances). llvm::DenseMap SynthesizedConformances; private: /// Describe the candidate expression for partial solving. /// This class used by shrink & solve methods which apply /// variation of directional path consistency algorithm in attempt /// to reduce scopes of the overload sets (disjunctions) in the system. class Candidate { Expr *E; DeclContext *DC; llvm::BumpPtrAllocator &Allocator; // Contextual Information. Type CT; ContextualTypePurpose CTP; public: Candidate(ConstraintSystem &cs, Expr *expr, Type ct = Type(), ContextualTypePurpose ctp = ContextualTypePurpose::CTP_Unused) : E(expr), DC(cs.DC), Allocator(cs.Allocator), CT(ct), CTP(ctp) {} /// Return underlying expression. Expr *getExpr() const { return E; } /// Try to solve this candidate sub-expression /// and re-write it's OSR domains afterwards. /// /// \param shrunkExprs The set of expressions which /// domains have been successfully shrunk so far. /// /// \returns true on solver failure, false otherwise. bool solve(llvm::SmallSetVector &shrunkExprs); /// Apply solutions found by solver as reduced OSR sets for /// for current and all of it's sub-expressions. /// /// \param solutions The solutions found by running solver on the /// this candidate expression. /// /// \param shrunkExprs The set of expressions which /// domains have been successfully shrunk so far. void applySolutions( llvm::SmallVectorImpl &solutions, llvm::SmallSetVector &shrunkExprs) const; /// Check if attempt at solving of the candidate makes sense given /// the current conditions - number of shrunk domains which is related /// to the given candidate over the total number of disjunctions present. static bool isTooComplexGiven(ConstraintSystem *const cs, llvm::SmallSetVector &shrunkExprs) { SmallVector disjunctions; cs->collectDisjunctions(disjunctions); unsigned unsolvedDisjunctions = disjunctions.size(); for (auto *disjunction : disjunctions) { auto *locator = disjunction->getLocator(); if (!locator) continue; if (auto *OSR = getAsExpr(locator->getAnchor())) { if (shrunkExprs.count(OSR) > 0) --unsolvedDisjunctions; } } // The threshold used to be `TypeCheckerOpts.SolverShrinkUnsolvedThreshold` return unsolvedDisjunctions >= 10; } }; /// 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: /// Retrieve the first constraint that has failed along the solver's path, or /// \c nullptr if no constraint has failed. Constraint *getFailedConstraint() const { return failedConstraint; } ConstraintSystemPhase getPhase() const { return Phase; } /// Move constraint system to a new phase of its lifetime. void setPhase(ConstraintSystemPhase newPhase) { if (Phase == newPhase) return; #ifndef NDEBUG switch (Phase) { case ConstraintSystemPhase::ConstraintGeneration: assert(newPhase == ConstraintSystemPhase::Solving); break; case ConstraintSystemPhase::Solving: // We can come back to constraint generation phase while // processing result builder body. assert(newPhase == ConstraintSystemPhase::ConstraintGeneration || newPhase == ConstraintSystemPhase::Diagnostics || newPhase == ConstraintSystemPhase::Finalization); break; case ConstraintSystemPhase::Diagnostics: assert(newPhase == ConstraintSystemPhase::Solving || newPhase == ConstraintSystemPhase::Finalization); break; case ConstraintSystemPhase::Finalization: assert(newPhase == ConstraintSystemPhase::Diagnostics); break; } #endif Phase = newPhase; } /// 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) { ASSERT(!node.isNull() && "Cannot set type information on null node"); ASSERT(type && "Expected non-null type"); // Record the type. Type &entry = NodeTypes[node]; Type oldType = entry; entry = type; if (oldType.getPointer() != type.getPointer()) { // Record the fact that we assigned a type to this node. if (solverState) recordChange(SolverTrail::Change::RecordedNodeType(node, oldType)); } } /// Undo the above change. void restoreType(ASTNode node, Type oldType) { ASSERT(!node.isNull() && "Cannot set type information on null node"); if (oldType) { auto found = NodeTypes.find(node); ASSERT(found != NodeTypes.end()); found->second = oldType; } else { bool erased = NodeTypes.erase(node); ASSERT(erased); } } /// 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) { ASSERT(KP && "Expected non-null key path parameter!"); ASSERT(T && "Expected non-null type!"); Type &entry = KeyPathComponentTypes[{KP, I}]; Type oldType = entry; entry = T; if (oldType.getPointer() != T.getPointer()) { if (solverState) { recordChange( SolverTrail::Change::RecordedKeyPathComponentType( KP, I, oldType)); } } } void restoreType(const KeyPathExpr *KP, unsigned I, Type T) { ASSERT(KP && "Expected non-null key path parameter!"); if (T) { auto found = KeyPathComponentTypes.find({KP, I}); ASSERT(found != KeyPathComponentTypes.end()); found->second = T; } else { bool erased = KeyPathComponentTypes.erase({KP, I}); ASSERT(erased); } } 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(hasType(node) && "Expected type to have been set!"); // FIXME: lvalue differences // assert((!E->getType() || // E->getType()->isEqual(ExprTypes.find(E)->second)) && // "Mismatched types!"); return NodeTypes.find(node)->second; } Type getType(const KeyPathExpr *KP, unsigned I) const { assert(hasType(KP, I) && "Expected type to have been set!"); return KeyPathComponentTypes.find(std::make_pair(KP, I))->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; } /// Retrieve type of the given declaration to be used in /// constraint system, this is better than calling `getType()` /// directly because it accounts of constraint system flags. Type getVarType(const VarDecl *var); /// 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); /// Compute a constraint locator for an implicit value-to-value /// conversion rooted at the given location. ConstraintLocator * getImplicitValueConversionLocator(ConstraintLocatorBuilder root, ConversionRestrictionKind restriction); /// 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); } /// Check whether old type-checker performance hacks has been explicitly /// enabled. bool performanceHacksEnabled() const { return Options.contains(ConstraintSystemFlags::EnablePerformanceHacks); } /// 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); void recordImplicitCallAsFunctionRoot( ConstraintLocator *locator, UnresolvedDotExpr *root); /// 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; }); } /// If an UnresolvedDotExpr, SubscriptMember, etc has been resolved by the /// constraint system, return the decl that it references. ValueDecl *findResolvedMemberRef(ConstraintLocator *locator); /// 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); /// Convenience function to pass an \c ArrayRef to \c addJoinConstraint Type addJoinConstraint(ConstraintLocator *locator, ArrayRef> inputs, std::optional supertype = std::nullopt) { return addJoinConstraint( locator, inputs.begin(), inputs.end(), supertype, [](auto it) { return *it; }); } /// Add a "join" constraint between a set of types, producing the common /// supertype. /// /// Currently, a "join" is modeled by a set of conversion constraints to /// a new type variable or a specified supertype. At some point, we may want /// a new constraint kind to cover the join. /// /// \note This method will merge any input type variables for atomic literal /// expressions of the same kind. It assumes that if same-kind literal type /// variables are joined, there will be no differing constraints on those /// type variables. /// /// \returns the joined type, which is generally a new type variable, unless there are /// fewer than 2 input types or the \c supertype parameter is specified. template Type addJoinConstraint( ConstraintLocator *locator, Iterator begin, Iterator end, std::optional supertype, std::function(Iterator)> getType) { if (begin == end) return Type(); // No need to generate a new type variable if there's only one type to join if ((begin + 1 == end) && !supertype.has_value()) return getType(begin).first; // The type to capture the result of the join, which is either the specified supertype, // or a new type variable. Type resultTy = supertype.has_value() ? supertype.value() : createTypeVariable(locator, (TVO_PrefersSubtypeBinding | TVO_CanBindToNoEscape)); using RawExprKind = uint8_t; llvm::SmallDenseMap representativeForKind; // Join the input types. while (begin != end) { Type type; ConstraintLocator *locator; std::tie(type, locator) = getType(begin++); // We can merge the type variables of same-kind atomic literal expressions because they // will all have the same set of constraints and therefore can never resolve to anything // different. if (auto *typeVar = type->getAs()) { if (auto literalKind = typeVar->getImpl().getAtomicLiteralKind()) { auto *&originalRep = representativeForKind[RawExprKind(*literalKind)]; auto *currentRep = getRepresentative(typeVar); if (originalRep) { if (originalRep != currentRep) mergeEquivalenceClasses(currentRep, originalRep, /*updateWorkList=*/false); continue; } originalRep = currentRep; } } // Introduce conversions from each input type to the supertype. addConstraint(ConstraintKind::Conversion, type, resultTy, locator); } return resultTy; } /// 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(locator, boundTy, choice, useDC, /*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 a value witness constraint to the constraint system. void addValueWitnessConstraint( Type baseTy, ValueDecl *requirement, Type memberTy, DeclContext *useDC, FunctionRefInfo functionRefInfo, ConstraintLocatorBuilder locator) { assert(baseTy); assert(memberTy); assert(requirement); assert(useDC); switch (simplifyValueWitnessConstraint( ConstraintKind::ValueWitness, baseTy, requirement, memberTy, useDC, functionRefInfo, TMF_GenerateConstraints, locator)) { case SolutionKind::Unsolved: llvm_unreachable("Unsolved result when generating constraints!"); case SolutionKind::Solved: case SolutionKind::Error: 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() && getPhase() == ConstraintSystemPhase::Solving) { 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() && getPhase() == ConstraintSystemPhase::Solving) { 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 { return typeVar->getImpl().getRepresentative(getTrail()); } /// 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 VarDecl associated with resolvedOverload, and the type of the /// projection if the decl has an associated property wrapper with a projectedValue. std::optional> getPropertyWrapperProjectionInfo(SelectedOverload resolvedOverload); /// Gets the VarDecl associated with resolvedOverload, and the type of the /// backing storage if the decl has an associated property wrapper. std::optional> getPropertyWrapperInformation(SelectedOverload resolvedOverload); /// Gets the VarDecl, and the type of the type property that it wraps if /// resolved overload has a decl which is the backing storage for a /// property wrapper. std::optional> getWrappedPropertyInformation(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 { return typeVar->getImpl().getFixedType(getTrail()); } /// 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, const Requirement &requirement, bool skipProtocolSelfConstraint, 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); /// 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, ArrayRef replacements, ConstraintLocatorBuilder locator, PreparedOverloadBuilder *preparedOverload); /// 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. /// /// \param decl The declarations whose type is being computed. /// /// \returns a description of the type of this declaration reference. DeclReferenceType getTypeOfReference( ValueDecl *decl, FunctionRefInfo functionRefInfo, ConstraintLocatorBuilder locator, DeclContext *useDC, 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 = false, bool adjustForPreconcurrency = true); /// Given the opened type and a pile of information about a member reference, /// determine the reference type of the member reference. Type getMemberReferenceTypeFromOpenedType( Type &openedType, Type baseObjTy, ValueDecl *value, DeclContext *outerDC, ConstraintLocator *locator, bool hasAppliedSelf, bool isDynamicLookup, ArrayRef replacements); /// 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. /// /// \param isDynamicLookup Indicates that this declaration was found via /// dynamic lookup. /// /// \returns a description of the type of this declaration reference. DeclReferenceType getTypeOfMemberReference( Type baseTy, ValueDecl *decl, DeclContext *useDC, bool isDynamicLookup, FunctionRefInfo functionRefInfo, ConstraintLocator *locator, SmallVectorImpl *replacements = nullptr, PreparedOverloadBuilder *preparedOverload = nullptr); /// 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: /// 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, bool allowMembers, DeclContext *useDC); /// Add a new overload set to the list of unresolved overload /// sets. void addOverloadSet(Type boundType, ArrayRef choices, DeclContext *useDC, ConstraintLocator *locator, std::optional favoredIndex = std::nullopt); 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 favoredIndex If there is a "favored" or preferred choice /// this is its index in the set of choices. /// /// \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, std::optional favoredIndex = std::nullopt, 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); /// 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(), 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); 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(ConstraintLocator *locator, OverloadChoice choice, DeclContext *useDC); /// Populate the prepared overload with all type variables and constraints /// that are to be introduced into the constraint system when this choice /// is taken. DeclReferenceType prepareOverloadImpl(ConstraintLocator *locator, OverloadChoice choice, DeclContext *useDC, PreparedOverloadBuilder *preparedOverload); void replayChanges( ConstraintLocator *locator, PreparedOverload *preparedOverload); /// Resolve the given overload set to the given choice. void resolveOverload(ConstraintLocator *locator, Type boundType, OverloadChoice choice, DeclContext *useDC, 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); /// Attempt to simplify the given value witness constraint. SolutionKind simplifyValueWitnessConstraint( ConstraintKind kind, Type baseType, ValueDecl *member, Type memberType, DeclContext *useDC, FunctionRefInfo functionRefInfo, 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. std::optional determineBestBindings( llvm::function_ref onCandidate); /// Get bindings for the given type variable based on current /// state of the constraint system. /// /// FIXME: Remove this. 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); /// Collect the current inactive disjunction constraints. void collectDisjunctions(SmallVectorImpl &disjunctions); /// 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); /// 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); 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); /// Find reduced domains of disjunction constraints for given /// expression, this is achieved to solving individual sub-expressions /// and combining resolving types. Such algorithm is called directional /// path consistency because it goes from children to parents for all /// related sub-expressions taking union of their domains. /// /// \param expr The expression to find reductions for. void shrink(Expr *expr); /// Pick a disjunction from the InactiveConstraints list. /// /// \returns The selected disjunction and a set of it's favored choices. std::optional>> selectDisjunction(); /// The old method that is only used when performance hacks are enabled. Constraint *selectDisjunctionWithHacks(); /// Pick a conjunction from the InactiveConstraints list. /// /// \returns The selected conjunction. Constraint *selectConjunction(); /// 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); 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); public: /// 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); /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully /// resolved before any others. void optimizeConstraints(Expr *e); void startExpressionTimer(ExpressionTimer::AnchorType anchor); /// 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) { if (isAlreadyTooComplex.first) return true; auto CancellationFlag = getASTContext().CancellationFlag; if (CancellationFlag && CancellationFlag->load(std::memory_order_relaxed)) return true; auto used = getASTContext().getSolverMemory() + solutionMemory; MaxMemory = std::max(used, MaxMemory); auto threshold = getASTContext().TypeCheckerOpts.SolverMemoryThreshold; if (MaxMemory > threshold) { // No particular location for OoM problems. isAlreadyTooComplex.first = true; return true; } if (Timer && Timer->isExpired()) { // Disable warnings about expressions that go over the warning // threshold since we're arbitrarily ending evaluation and // emitting an error. Timer->disableWarning(); isAlreadyTooComplex = {true, Timer->getAffectedRange()}; return true; } auto &opts = getASTContext().TypeCheckerOpts; // Bail out once we've looked at a really large number of choices. if (opts.SolverScopeThreshold && NumSolverScopes > opts.SolverScopeThreshold) { isAlreadyTooComplex.first = true; return true; } // Bail out once we've taken a really large number of steps. if (opts.SolverTrailThreshold && NumTrailSteps > opts.SolverTrailThreshold) { isAlreadyTooComplex.first = true; return true; } return false; } 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(SyntacticElementTarget target) 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 OpenUnboundGenericTypeFn that /// "opens" the given unbound type by introducing fresh type variables for /// generic parameters and constructing a bound generic type from these /// type variables. class OpenUnboundGenericType { ConstraintSystem &cs; const ConstraintLocatorBuilder &locator; public: explicit OpenUnboundGenericType(ConstraintSystem &cs, const ConstraintLocatorBuilder &locator) : cs(cs), locator(locator) {} Type operator()(UnboundGenericType *unboundTy) const { return cs.openUnboundGenericType(unboundTy->getDecl(), unboundTy->getParent(), locator, /*isTypeResolution=*/true); } }; class HandlePlaceholderType { ConstraintSystem &cs; ConstraintLocator *locator; public: explicit HandlePlaceholderType(ConstraintSystem &cs, const ConstraintLocatorBuilder &locator) : cs(cs) { this->locator = cs.getConstraintLocator(locator); } Type operator()(ASTContext &ctx, PlaceholderTypeRepr *placeholderRepr) const { return cs.createTypeVariable( cs.getConstraintLocator( locator, LocatorPathElt::PlaceholderType(placeholderRepr)), TVO_CanBindToNoEscape | TVO_PrefersSubtypeBinding | TVO_CanBindToHole); } }; /// A function object that opens a given pack type by generating a /// \c PackElementOf constraint. class OpenPackElementType { ConstraintSystem &cs; ConstraintLocator *locator; PackExpansionExpr *elementEnv; public: explicit OpenPackElementType(ConstraintSystem &cs, const ConstraintLocatorBuilder &locator, PackExpansionExpr *elementEnv) : cs(cs), elementEnv(elementEnv) { this->locator = cs.getConstraintLocator(locator); } Type operator()(Type packType, PackElementTypeRepr *packRepr) const { // Only assert we have an element environment when invoking the function // object. In cases where pack elements are referenced outside of a // pack expansion, type resolution will error before opening the pack // element. assert(elementEnv); auto *elementType = cs.createTypeVariable(locator, TVO_CanBindToHole | TVO_CanBindToNoEscape); // If we're opening a pack element from an explicit type repr, // set the type repr types in the constraint system for generating // ShapeOf constraints when visiting the PackExpansionExpr. if (packRepr) { cs.setType(packRepr->getPackType(), packType); cs.setType(packRepr, elementType); } cs.addConstraint(ConstraintKind::PackElementOf, elementType, packType->getRValueType(), cs.getConstraintLocator(elementEnv)); return elementType; } }; /// 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); class DisjunctionChoice { ConstraintSystem &CS; unsigned Index; Constraint *Choice; bool ExplicitConversion; bool IsBeginningOfPartition; public: DisjunctionChoice(ConstraintSystem &cs, unsigned index, Constraint *choice, bool explicitConversion, bool isBeginningOfPartition) : CS(cs), Index(index), Choice(choice), ExplicitConversion(explicitConversion), IsBeginningOfPartition(isBeginningOfPartition) {} unsigned getIndex() const { return Index; } bool attempt(ConstraintSystem &cs) const; bool isDisabled() const { if (!Choice->isDisabled()) return false; // If solver is in a diagnostic mode, let's allow // constraints that have fixes or have been disabled // in attempt to produce a solution faster for // well-formed expressions. if (CS.shouldAttemptFixes()) { return !(hasFix() || Choice->isDisabledInPerformanceMode()); } return true; } bool hasFix() const { return bool(Choice->getFix()); } bool isUnavailable() const { if (auto *decl = getOverloadChoiceDecl(Choice)) return CS.isDeclUnavailable(decl, Choice->getLocator()); return false; } bool isDisfavored() const { if (auto *decl = getOverloadChoiceDecl(Choice)) return decl->getAttrs().hasAttribute(); return false; } bool isBeginningOfPartition() const { return IsBeginningOfPartition; } // FIXME: All three of the accessors below are required to support // performance optimization hacks in constraint solver. bool isGenericOperator() const; bool isSymmetricOperator() const; bool isUnaryOperator() const; void print(llvm::raw_ostream &Out, SourceManager *SM, unsigned indent = 0) const { Out << "disjunction choice "; Choice->print(Out, SM, indent); } operator Constraint *() { return Choice; } operator Constraint *() const { return Choice; } private: /// If associated disjunction is an explicit conversion, /// let's try to propagate its type early to prune search space. void propagateConversionInfo(ConstraintSystem &cs) const; static ValueDecl *getOperatorDecl(Constraint *choice) { auto *decl = getOverloadChoiceDecl(choice); if (!decl) return nullptr; return decl->isOperator() ? decl : nullptr; } }; class ConjunctionElement { Constraint *Element; public: ConjunctionElement(Constraint *element) : Element(element) {} bool attempt(ConstraintSystem &cs) const; ConstraintLocator *getLocator() const { return Element->getLocator(); } void print(llvm::raw_ostream &Out, SourceManager *SM, unsigned indent) const { Out << "conjunction element "; Element->print(Out, SM, indent); } private: /// Find type variables referenced by this conjunction element. /// If this is a closure body element, it would look inside \c ASTNode. void findReferencedVariables(ConstraintSystem &cs, SmallPtrSetImpl &typeVars) const; }; class TypeVariableBinding { TypeVariableType *TypeVar; PotentialBinding Binding; public: TypeVariableBinding(TypeVariableType *typeVar, PotentialBinding &binding) : TypeVar(typeVar), Binding(binding) {} TypeVariableType *getTypeVariable() const { return TypeVar; } Type getType() const { return Binding.BindingType; } bool isDefaultable() const { return Binding.isDefaultableBinding(); } bool hasDefaultedProtocol() const { return Binding.hasDefaultedLiteralProtocol(); } bool attempt(ConstraintSystem &cs) const; /// Determine what fix (if any) needs to be introduced into a /// constraint system as part of resolving type variable as a hole. std::optional> fixForHole(ConstraintSystem &cs) const; void print(llvm::raw_ostream &Out, SourceManager *, unsigned indent) const { PrintOptions PO; PO.PrintTypesForDebugging = true; Out << "type variable binding " << TypeVar->getString(PO) << " := " << Binding.BindingType->getString(PO); } }; template class BindingProducer { ConstraintLocator *Locator; protected: ConstraintSystem &CS; public: BindingProducer(ConstraintSystem &cs, ConstraintLocator *locator) : Locator(locator), CS(cs) {} virtual ~BindingProducer() {} virtual std::optional operator()() = 0; ConstraintLocator *getLocator() const { return Locator; } /// Check whether generator would have to compute next /// batch of bindings because it freshly ran out of current one. /// This is useful to be able to exhaustively attempt bindings /// for type variables found at one level, before proceeding to /// supertypes or literal defaults etc. virtual bool needsToComputeNext() const = 0; virtual bool isExhausted() const = 0; }; class TypeVarBindingProducer : public BindingProducer { using BindingKind = AllowedBindingKind; using Binding = PotentialBinding; TypeVariableType *TypeVar; llvm::SmallVector Bindings; /// The set of defaults to attempt once producer /// runs out of direct & transitive bindings. llvm::SmallVector DelayedDefaults; // The index pointing to the offset in the bindings // generator is currently at, `numTries` represents // the number of times bindings have been recomputed. unsigned Index = 0, NumTries = 0; llvm::SmallPtrSet ExploredTypes; llvm::SmallPtrSet BoundTypes; /// Determines whether this type variable has a /// `ExpressibleByNilLiteral` requirement which /// means that bindings have to either conform /// to that protocol or be wrapped in an optional. bool CanBeNil; bool IsExhausted = false; public: using Element = TypeVariableBinding; TypeVarBindingProducer(BindingSet &bindings); /// Retrieve a set of bindings available in the current state. ArrayRef getCurrentBindings() const { return Bindings; } std::optional operator()() override { if (isExhausted()) return std::nullopt; // Once we reach the end of the current bindings // let's try to compute new ones, e.g. supertypes, // literal defaults, if that fails, we are done. if (needsToComputeNext() && !computeNext()) { IsExhausted = true; return std::nullopt; } auto &binding = Bindings[Index++]; // Record produced type as bound/explored early, otherwise // it could be possible to re-discover it during `computeNext()`, // which leads to duplicate bindings e.g. inferring fallback // `Void` for a closure result type when `Void` was already // inferred as a direct/transitive binding. { auto type = binding.BindingType; BoundTypes.insert(type.getPointer()); ExploredTypes.insert(type->getCanonicalType()); } return TypeVariableBinding(TypeVar, binding); } bool needsToComputeNext() const override { return isExhausted() ? false : Index >= Bindings.size(); } bool isExhausted() const override { return IsExhausted; } private: /// Compute next batch of bindings if possible, this could /// be supertypes extracted from one of the current bindings /// or default literal types etc. /// /// \returns true if some new bindings were successfully computed, /// false otherwise. bool computeNext(); /// Check whether binding type is required to either conform to /// `ExpressibleByNilLiteral` protocol or be wrapped into an optional type. bool requiresOptionalAdjustment(const Binding &binding) const; Binding getDefaultBinding(Constraint *constraint) const; }; /// Iterator over disjunction choices, makes it /// easy to work with disjunction and encapsulates /// some other important information such as locator. class DisjunctionChoiceProducer : public BindingProducer { // The disjunction choices that this producer will iterate through. ArrayRef Choices; // The ordering of disjunction choices. We index into Choices // through this vector in order to visit the disjunction choices in // the order we want to visit them. SmallVector Ordering; // The index of the first element in a partition of the disjunction // choices. The choices are split into partitions where we will // visit all elements within a single partition before moving to the // elements of the next partition. If we visit all choices within a // single partition and have found a successful solution with one of // the choices in that partition, we stop looking for other // solutions. SmallVector PartitionBeginning; // The index in the current partition of disjunction choices that we // are iterating over. unsigned PartitionIndex = 0; bool IsExplicitConversion; Constraint *Disjunction; unsigned Index = 0; bool needsGenericOperatorOrdering = true; public: using Element = DisjunctionChoice; DisjunctionChoiceProducer(ConstraintSystem &cs, Constraint *disjunction, llvm::TinyPtrVector &favorites) : BindingProducer(cs, disjunction->shouldRememberChoice() ? disjunction->getLocator() : nullptr), Choices(disjunction->getNestedConstraints()), IsExplicitConversion(disjunction->isExplicitConversion()), Disjunction(disjunction) { assert(disjunction->getKind() == ConstraintKind::Disjunction); assert(!disjunction->shouldRememberChoice() || disjunction->getLocator()); // Mark constraints as favored. This information // is going to be used by partitioner. for (auto *choice : favorites) cs.favorConstraint(choice); // Order and partition the disjunction choices. partitionDisjunction(Ordering, PartitionBeginning); } void setNeedsGenericOperatorOrdering(bool flag) { needsGenericOperatorOrdering = flag; } std::optional operator()() override { if (isExhausted()) return std::nullopt; unsigned currIndex = Index; bool isBeginningOfPartition = PartitionIndex < PartitionBeginning.size() && PartitionBeginning[PartitionIndex] == Index; if (isBeginningOfPartition) ++PartitionIndex; ++Index; auto choice = DisjunctionChoice(CS, currIndex, Choices[Ordering[currIndex]], IsExplicitConversion, isBeginningOfPartition); // Partition the generic operators before producing the first generic // operator disjunction choice. if (needsGenericOperatorOrdering && choice.isGenericOperator()) { unsigned nextPartitionIndex = (PartitionIndex < PartitionBeginning.size() ? PartitionBeginning[PartitionIndex] : Ordering.size()); partitionGenericOperators(Ordering.begin() + currIndex, Ordering.begin() + nextPartitionIndex); needsGenericOperatorOrdering = false; } return DisjunctionChoice(CS, currIndex, Choices[Ordering[currIndex]], IsExplicitConversion, isBeginningOfPartition); } bool needsToComputeNext() const override { return false; } bool isExhausted() const override { return Index >= Choices.size(); } private: // Partition the choices in the disjunction into groups that we will // iterate over in an order appropriate to attempt to stop before we // have to visit all of the options. void partitionDisjunction(SmallVectorImpl &Ordering, SmallVectorImpl &PartitionBeginning); /// Partition the choices in the range \c first to \c last into groups and /// order the groups in the best order to attempt based on the argument /// function type that the operator is applied to. void partitionGenericOperators(SmallVectorImpl::iterator first, SmallVectorImpl::iterator last); }; class ConjunctionElementProducer : public BindingProducer { ArrayRef Elements; unsigned Index = 0; public: using Element = ConjunctionElement; ConjunctionElementProducer(ConstraintSystem &cs, Constraint *conjunction) : BindingProducer(cs, conjunction->getLocator()), Elements(conjunction->getNestedConstraints()) { assert(conjunction->getKind() == ConstraintKind::Conjunction); } std::optional operator()() override { if (Index >= Elements.size()) return std::nullopt; return ConjunctionElement(Elements[Index++]); } bool needsToComputeNext() const override { return false; } bool isExhausted() const override { return Index >= Elements.size(); } void markExhausted() { Index = Elements.size(); } }; /// 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 given type is a known one /// for a key path `{Any, Partial, Writable, ReferenceWritable}KeyPath`. bool isKnownKeyPathType(Type type); /// 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); } // end namespace constraints template TypeVariableType *TypeVariableType::getNew(const ASTContext &C, unsigned ID, Args &&...args) { // Allocate memory void *mem = C.Allocate(sizeof(TypeVariableType) + sizeof(Implementation), alignof(TypeVariableType), AllocationArena::ConstraintSolver); // Construct the type variable. auto *result = ::new (mem) TypeVariableType(C, ID); // Construct the implementation object. new (result+1) TypeVariableType::Implementation(std::forward(args)...); return result; } /// 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); // Count the number of overload sets present // in the expression and all of the children. class OverloadSetCounter : public ASTWalker { unsigned &NumOverloads; public: OverloadSetCounter(unsigned &overloads) : NumOverloads(overloads) {} MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Arguments; } PreWalkResult walkToExprPre(Expr *expr) override { if (auto applyExpr = dyn_cast(expr)) { // If we've found function application and it's // function is an overload set, count it. if (isa(applyExpr->getFn())) ++NumOverloads; } // Always recur into the children. return Action::Continue(expr); } }; /// Matches array of function parameters to candidate inputs, /// which can be anything suitable (e.g., parameters, arguments). /// /// It claims inputs sequentially and tries to pair between an input /// and the next appropriate parameter. The detailed matching behavior /// of each pair is specified by a custom function (i.e., pairMatcher). /// It considers variadic and defaulted arguments when forming proper /// input-parameter pairs; however, other information like label and /// type information is not directly used here. It can be implemented /// in the custom function when necessary. class InputMatcher { size_t NumSkippedParameters; const ParameterListInfo &ParamInfo; const ArrayRef Params; public: enum Result { /// The specified inputs are successfully matched. IM_Succeeded, /// There are input(s) left unclaimed while all parameters are matched. IM_HasUnclaimedInput, /// There are parateter(s) left unmatched while all inputs are claimed. IM_HasUnmatchedParam, /// Custom pair matcher function failure. IM_CustomPairMatcherFailed, }; InputMatcher(const ArrayRef params, const ParameterListInfo ¶mInfo); /// Matching a given array of inputs. /// /// \param numInputs The number of inputs. /// \param pairMatcher Custom matching behavior of an input-parameter pair. /// \return the matching result. Result match(int numInputs, std::function pairMatcher); size_t getNumSkippedParameters() const { return NumSkippedParameters; } }; // 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 // LLVM_SWIFT_SEMA_CONSTRAINT_SYSTEM_H