mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We already had bookkeeping to track which statement in a multi-statement closure we were looking at, but this was only used for the 'reasonable time' diagnostic in the case that we hit the expression timer, which was almost never hit, and is now off by default. The scope, memory, and trial limits couldn't use this information, so they would always diagnose the entire target being type checked. Move it up from ExpressionTimer to ConstraintSystem, so that we get the right source location there too. Also, factor out some code duplication in BuilderTransform to ensure we get the same benefit for result builders applied to function bodies too.
6664 lines
248 KiB
C++
6664 lines
248 KiB
C++
//===--- 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 <cstddef>
|
|
#include <functional>
|
|
|
|
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<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,
|
|
Type builderType);
|
|
|
|
std::optional<constraints::SyntacticElementTarget>
|
|
typeCheckExpression(constraints::SyntacticElementTarget &target,
|
|
OptionSet<TypeCheckExprFlags> options,
|
|
DiagnosticTransaction *diagnosticTransaction = nullptr);
|
|
|
|
std::optional<constraints::SyntacticElementTarget>
|
|
typeCheckTarget(constraints::SyntacticElementTarget &target,
|
|
OptionSet<TypeCheckExprFlags> 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<DeclName, ResultBuilderOpSupport> 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<Identifier> 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<Expr *> argExprs,
|
|
ArrayRef<Identifier> 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<ConversionRestrictionKind> getRestriction() const {
|
|
if (IsRestriction)
|
|
return Restriction;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<ConstraintFix *> getFix() const {
|
|
if (!IsRestriction)
|
|
return TheFix;
|
|
|
|
return std::nullopt;
|
|
}
|
|
};
|
|
|
|
|
|
class ExpressionTimer {
|
|
ConstraintSystem &CS;
|
|
llvm::TimeRecord StartTime;
|
|
|
|
/// The number of seconds from creation until
|
|
/// this timer is considered expired.
|
|
unsigned ThresholdInSecs;
|
|
|
|
bool PrintWarning;
|
|
|
|
public:
|
|
const static unsigned NoLimit = (unsigned) -1;
|
|
|
|
ExpressionTimer(ConstraintSystem &CS, unsigned thresholdInSecs);
|
|
|
|
~ExpressionTimer();
|
|
|
|
unsigned getWarnLimit() const;
|
|
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<KeyPathMutability, /*isSendable=*/bool>;
|
|
|
|
/// 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<TypeVariableType *, TypeBase *> ParentOrFixed;
|
|
|
|
/// The corresponding node in the constraint graph.
|
|
constraints::ConstraintGraphNode *GraphNode = nullptr;
|
|
|
|
/// Temporary state for ConstraintGraph::computeConnectedComponents(),
|
|
/// stored inline for performance.
|
|
llvm::PointerIntPair<TypeVariableType *, 1, unsigned> Component;
|
|
|
|
friend class constraints::SolverTrail;
|
|
|
|
public:
|
|
/// Retrieve the type variable associated with this implementation.
|
|
TypeVariableType *getTypeVariable() const {
|
|
return reinterpret_cast<TypeVariableType *>(
|
|
const_cast<Implementation *>(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 (!isa<TypeVariableType *>(ParentOrFixed))
|
|
return true;
|
|
|
|
// Check whether the representative is different from our own type
|
|
// variable.
|
|
return cast<TypeVariableType *>(ParentOrFixed) != getTypeVariable();
|
|
}
|
|
|
|
/// Low-level accessor; use getRepresentative() or getFixedType() instead.
|
|
llvm::PointerUnion<TypeVariableType *, TypeBase *>
|
|
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<ExprKind> 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 (isa<TypeVariableType *>(impl->ParentOrFixed)) {
|
|
// Extract the representative.
|
|
auto nextTV = cast<TypeVariableType *>(impl->ParentOrFixed);
|
|
if (nextTV == result)
|
|
break;
|
|
|
|
result = nextTV;
|
|
impl = &nextTV->getImpl();
|
|
}
|
|
|
|
if (impl == this || !trail || trail->isUndoActive())
|
|
return result;
|
|
|
|
// Perform path compression.
|
|
impl = this;
|
|
while (isa<TypeVariableType *>(impl->ParentOrFixed)) {
|
|
// Extract the representative.
|
|
auto nextTV = cast<TypeVariableType *>(impl->ParentOrFixed);
|
|
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<TypeBase *>();
|
|
}
|
|
|
|
/// 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<TypeVariableType::Implementation *>(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 <typename T = Expr> T *castToExpr(ASTNode node) {
|
|
return cast<T>(cast<Expr *>(node));
|
|
}
|
|
|
|
template <typename T = Expr> T *getAsExpr(ASTNode node) {
|
|
if (node.isNull())
|
|
return nullptr;
|
|
|
|
if (auto *E = node.dyn_cast<Expr *>())
|
|
return dyn_cast_or_null<T>(E);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T> bool isExpr(ASTNode node) {
|
|
if (node.isNull() || !isa<Expr *>(node))
|
|
return false;
|
|
|
|
auto *E = cast<Expr *>(node);
|
|
return isa<T>(E);
|
|
}
|
|
|
|
template <typename T = Decl> T *getAsDecl(ASTNode node) {
|
|
if (auto *E = node.dyn_cast<Decl *>())
|
|
return dyn_cast_or_null<T>(E);
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T = TypeRepr>
|
|
T *getAsTypeRepr(ASTNode node) {
|
|
if (auto *type = node.dyn_cast<TypeRepr *>())
|
|
return dyn_cast_or_null<T>(type);
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T = Stmt>
|
|
T *getAsStmt(ASTNode node) {
|
|
if (auto *S = node.dyn_cast<Stmt *>())
|
|
return dyn_cast_or_null<T>(S);
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T = Pattern>
|
|
T *getAsPattern(ASTNode node) {
|
|
if (auto *P = node.dyn_cast<Pattern *>())
|
|
return dyn_cast_or_null<T>(P);
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename T = Stmt> T *castToStmt(ASTNode node) {
|
|
return cast<T>(cast<Stmt *>(node));
|
|
}
|
|
|
|
SourceLoc getLoc(ASTNode node);
|
|
SourceRange getSourceRange(ASTNode node);
|
|
|
|
/// The result of comparing two constraint systems that are a solutions
|
|
/// to the given set of constraints.
|
|
enum class SolutionCompareResult {
|
|
/// The two solutions are incomparable, because, e.g., because one
|
|
/// solution has some better decisions and some worse decisions than the
|
|
/// other.
|
|
Incomparable,
|
|
/// The two solutions are identical.
|
|
Identical,
|
|
/// The first solution is better than the second.
|
|
Better,
|
|
/// The second solution is better than the first.
|
|
Worse
|
|
};
|
|
|
|
/// 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<FunctionArgApplyInfo>
|
|
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<char> &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<Identifier, 4> 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<FunctionType>())
|
|
paramTy = paramTy->castTo<FunctionType>()->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<AnyFunctionType>();
|
|
if (!interfaceFnTy) {
|
|
// If the interface type isn't a function, then just return the resolved
|
|
// parameter type.
|
|
return getParamType(lookThroughAutoclosure)->mapTypeOutOfEnvironment();
|
|
}
|
|
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 pointer conversion where the destination type is a generic parameter.
|
|
/// This should eventually be removed in favor of outright banning pointer
|
|
/// conversions for generic parameters. As such we consider it more impactful
|
|
/// than \c SK_ValueToPointerConversion.
|
|
SK_GenericParamPointerConversion,
|
|
/// 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_GenericParamPointerConversion:
|
|
return "pointer conversion to generic parameter";
|
|
|
|
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 << " <default ";
|
|
out << *this;
|
|
out << ">";
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Describes a dependent type that has been opened to a particular type
|
|
/// variable.
|
|
using OpenedType = std::pair<GenericTypeParamType *, TypeVariableType *>;
|
|
|
|
/// 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<unsigned>::getHashValue(static_cast<unsigned>(kind));
|
|
|
|
case Kind::stmtCondElement:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::getHashValue(storage.stmtCondElement));
|
|
|
|
case Kind::expr:
|
|
case Kind::closure:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::getHashValue(storage.expr));
|
|
|
|
case Kind::stmt:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::getHashValue(storage.stmt));
|
|
|
|
case Kind::pattern:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::getHashValue(storage.pattern));
|
|
|
|
case Kind::patternBindingEntry:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::getHashValue(
|
|
storage.patternBindingEntry.patternBinding),
|
|
DenseMapInfo<unsigned>::getHashValue(
|
|
storage.patternBindingEntry.index));
|
|
|
|
case Kind::varDecl:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::getHashValue(storage.varDecl));
|
|
|
|
case Kind::functionRef:
|
|
return hash_combine(
|
|
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
|
|
DenseMapInfo<void *>::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<unsigned, 1>;
|
|
|
|
/// 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<ParamBinding, 4> 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<SmallVector<ParamBinding, 4>> 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<ParamBinding, 4> 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<size_t> 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<TypeVariableType *, Type> typeBindings;
|
|
|
|
/// The set of overload choices along with their types.
|
|
llvm::DenseMap<ConstraintLocator *, SelectedOverload> overloadChoices;
|
|
|
|
/// The set of constraint restrictions used to arrive at this restriction,
|
|
/// which informs constraint application.
|
|
llvm::DenseMap<std::pair<CanType, CanType>, ConversionRestrictionKind>
|
|
ConstraintRestrictions;
|
|
|
|
/// The list of fixes that need to be applied to the initial expression
|
|
/// to make the solution work.
|
|
std::vector<ConstraintFix *> Fixes;
|
|
|
|
/// The list of fixed requirements.
|
|
using FixedRequirement =
|
|
std::tuple<GenericTypeParamType *, unsigned, TypeBase *>;
|
|
std::vector<FixedRequirement> 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<Expr *, ImpliedResultKind> ImpliedResults;
|
|
|
|
/// For locators associated with call expressions, the trailing closure
|
|
/// matching rule and parameter bindings that were applied.
|
|
llvm::DenseMap<ConstraintLocator *, MatchCallArgumentResult>
|
|
argumentMatchingChoices;
|
|
|
|
/// The set of disjunction choices used to arrive at this solution,
|
|
/// which informs constraint application.
|
|
llvm::DenseMap<ConstraintLocator *, unsigned> DisjunctionChoices;
|
|
|
|
/// A map from applied disjunction constraints to the corresponding
|
|
/// argument function type.
|
|
llvm::DenseMap<ConstraintLocator *, FunctionType *> AppliedDisjunctions;
|
|
|
|
/// The set of opened types for a given locator.
|
|
llvm::DenseMap<ConstraintLocator *, ArrayRef<OpenedType>> OpenedTypes;
|
|
|
|
/// The opened existential type for a given locator.
|
|
llvm::DenseMap<ConstraintLocator *, ExistentialArchetypeType *>
|
|
OpenedExistentialTypes;
|
|
|
|
llvm::DenseMap<PackExpansionType *, TypeVariableType *>
|
|
OpenedPackExpansionTypes;
|
|
|
|
/// The generic environment that can open pack elements for a given
|
|
/// pack expansion.
|
|
llvm::DenseMap<PackExpansionExpr *, GenericEnvironment *>
|
|
PackExpansionEnvironments;
|
|
|
|
/// The pack expansion expression for a given pack element.
|
|
llvm::DenseMap<PackElementExpr *, PackExpansionExpr *> PackElementExpansions;
|
|
|
|
/// The locators of \c Defaultable constraints whose defaults were used.
|
|
llvm::DenseSet<ConstraintLocator *> DefaultedConstraints;
|
|
|
|
/// The node -> type mappings introduced by this solution.
|
|
llvm::DenseMap<ASTNode, Type> nodeTypes;
|
|
|
|
/// The key path component types introduced by this solution.
|
|
llvm::DenseMap<std::pair<const KeyPathExpr *, unsigned>, Type>
|
|
keyPathComponentTypes;
|
|
|
|
/// The key path expression and its root type, value type, and decl context
|
|
/// introduced by this solution.
|
|
llvm::DenseMap<const KeyPathExpr *,
|
|
std::tuple</*root=*/TypeVariableType *,
|
|
/*value=*/TypeVariableType *, DeclContext *>>
|
|
KeyPaths;
|
|
|
|
/// Contextual types introduced by this solution.
|
|
std::vector<std::pair<ASTNode, ContextualTypeInfo>> contextualTypes;
|
|
|
|
/// Maps AST nodes to their target.
|
|
llvm::DenseMap<SyntacticElementTargetKey, SyntacticElementTarget> targets;
|
|
|
|
/// Maps case label items to information tracked about them as they are
|
|
/// being solved.
|
|
llvm::DenseMap<const CaseLabelItem *, CaseLabelItemInfo> 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<std::pair<CatchNode, PotentialThrowSite>>
|
|
potentialThrowSites;
|
|
|
|
/// A map of expressions to the ExprPatterns that they are being solved as
|
|
/// a part of.
|
|
llvm::DenseMap<Expr *, ExprPattern *> exprPatterns;
|
|
|
|
/// The set of parameters that have been inferred to be 'isolated'.
|
|
llvm::DenseSet<ParamDecl *> isolatedParams;
|
|
|
|
/// The set of closures that have been inferred to be "isolated by
|
|
/// preconcurrency".
|
|
llvm::DenseSet<const ClosureExpr *> preconcurrencyClosures;
|
|
|
|
/// The set of functions that have been transformed by a result builder.
|
|
llvm::MapVector<AnyFunctionRef, AppliedBuilderTransform>
|
|
resultBuilderTransformed;
|
|
|
|
/// A map from argument expressions to their applied property wrapper expressions.
|
|
llvm::DenseMap<ASTNode, SmallVector<AppliedPropertyWrapper, 2>> appliedPropertyWrappers;
|
|
|
|
ArrayRef<AppliedPropertyWrapper> getAppliedPropertyWrappers(ASTNode anchor) {
|
|
auto found = appliedPropertyWrappers.find(anchor);
|
|
if (found != appliedPropertyWrappers.end())
|
|
return found->second;
|
|
return ArrayRef<AppliedPropertyWrapper>();
|
|
}
|
|
|
|
/// 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<ConstraintLocator *, ArgumentList *> argumentLists;
|
|
|
|
/// The set of implicitly generated `.callAsFunction` root expressions.
|
|
llvm::DenseMap<ConstraintLocator *, UnresolvedDotExpr *>
|
|
ImplicitCallAsFunctionRoots;
|
|
|
|
/// The set of conformances synthesized during solving (i.e. for
|
|
/// ad-hoc distributed `SerializationRequirement` conformances).
|
|
llvm::DenseMap<ConstraintLocator *, ProtocolDecl *>
|
|
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.
|
|
/// \param forCompletion If true, will produce archetypes instead of
|
|
/// ErrorTypes for generic parameter originators, which is what completion
|
|
/// currently expects for the code completion token.
|
|
Type simplifyType(Type type, bool wantInterfaceType = false,
|
|
bool forCompletion = 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: P>(_: [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 `[<<hole>>]`
|
|
// 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<ValueDecl> 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<ImpliedResultKind> 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).value();
|
|
}
|
|
|
|
/// 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<SelectedOverload>
|
|
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<SelectedOverload>
|
|
getCalleeOverloadChoiceIfAvailable(ConstraintLocator *locator) const;
|
|
|
|
std::optional<SyntacticElementTarget>
|
|
getTargetFor(SyntacticElementTargetKey key) const;
|
|
|
|
ConstraintLocator *getCalleeLocator(ConstraintLocator *locator,
|
|
bool lookThroughApply = true) const;
|
|
|
|
ConstraintLocator *
|
|
getConstraintLocator(ASTNode anchor,
|
|
ArrayRef<LocatorPathElt> path = {}) const;
|
|
|
|
ConstraintLocator *getConstraintLocator(ConstraintLocator *baseLocator,
|
|
ArrayRef<LocatorPathElt> 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<ContextualTypeInfo>
|
|
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<FunctionArgApplyInfo>
|
|
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<ExprPattern> 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<ConversionRestrictionKind>
|
|
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<OverloadChoice, 2> choices;
|
|
};
|
|
|
|
/// The differences between the overload choices between the
|
|
/// solutions.
|
|
SmallVector<OverloadDiff, 4> overloads;
|
|
|
|
/// Compute the differences between the given set of solutions.
|
|
///
|
|
/// \param solutions The set of solutions.
|
|
explicit SolutionDiff(ArrayRef<Solution> solutions);
|
|
};
|
|
|
|
/// An intrusive, doubly-linked list of constraints.
|
|
using ConstraintList = llvm::ilist<Constraint>;
|
|
|
|
enum class ConstraintSystemFlags {
|
|
/// Whether we allow the solver to attempt fixes to the system.
|
|
AllowFixes = 0x01,
|
|
|
|
/// Set if the client wants diagnostics suppressed.
|
|
SuppressDiagnostics = 0x02,
|
|
|
|
/// If set, verbose output is enabled for this constraint system.
|
|
///
|
|
/// Note that this flag is automatically applied to all constraint systems,
|
|
/// when \c DebugConstraintSolver is set in \c TypeCheckerOptions. It can be
|
|
/// automatically enabled for select constraint solving attempts by setting
|
|
/// \c DebugConstraintSolverAttempt. Finally, it can also be automatically
|
|
/// enabled for a pre-configured set of expressions on line numbers by setting
|
|
/// \c DebugConstraintSolverOnLines.
|
|
DebugConstraints = 0x04,
|
|
|
|
/// If set, we are solving specifically to determine the type of a
|
|
/// CodeCompletionExpr, and should continue in the presence of errors wherever
|
|
/// possible.
|
|
ForCodeCompletion = 0x08,
|
|
|
|
/// Include Clang function types when checking equality for function types.
|
|
///
|
|
/// When LangOptions.UseClangFunctionTypes is set, we can synthesize
|
|
/// different @convention(c) function types with the same parameter and result
|
|
/// types (similarly for blocks). This difference is reflected in the `cType:`
|
|
/// field (@convention(c, cType: ...)). When the cType is different, the types
|
|
/// should be treated as semantically different, as they may have different
|
|
/// calling conventions, say due to Clang attributes such as
|
|
/// `__attribute__((ns_consumed))`.
|
|
UseClangFunctionTypes = 0x10,
|
|
|
|
/// When set, ignore async/sync mismatches
|
|
IgnoreAsyncSyncMismatch = 0x20,
|
|
|
|
/// Disable macro expansions.
|
|
DisableMacroExpansions = 0x40,
|
|
|
|
/// Enable old type-checker performance hacks.
|
|
EnablePerformanceHacks = 0x80,
|
|
};
|
|
|
|
/// Options that affect the constraint system as a whole.
|
|
using ConstraintSystemOptions = OptionSet<ConstraintSystemFlags>;
|
|
|
|
/// 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<OverloadChoice, 4> 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,
|
|
|
|
/// The module selector in the `DeclNameRef` does not match this candidate.
|
|
UR_WrongModule
|
|
};
|
|
|
|
/// This is a list of considered (but rejected) candidates, along with a
|
|
/// reason for their rejection. Split into separate collections to make
|
|
/// it easier to use in conjunction with viable candidates.
|
|
SmallVector<OverloadChoice, 4> UnviableCandidates;
|
|
SmallVector<UnviableReason, 4> 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<unsigned> getFavoredIndex() const {
|
|
return (FavoredChoice == ~0U) ? std::optional<unsigned>() : FavoredChoice;
|
|
}
|
|
};
|
|
|
|
/// Stores the required methods for @dynamicCallable types.
|
|
struct DynamicCallableMethods {
|
|
llvm::DenseSet<FuncDecl *> argumentsMethods;
|
|
llvm::DenseSet<FuncDecl *> keywordArgumentsMethods;
|
|
|
|
void addArgumentsMethod(FuncDecl *method) {
|
|
argumentsMethods.insert(method);
|
|
}
|
|
|
|
void addKeywordArgumentsMethod(FuncDecl *method) {
|
|
keywordArgumentsMethods.insert(method);
|
|
}
|
|
|
|
void addMethods(const DynamicCallableMethods &other) {
|
|
argumentsMethods.insert(other.argumentsMethods.begin(),
|
|
other.argumentsMethods.end());
|
|
keywordArgumentsMethods.insert(other.keywordArgumentsMethods.begin(),
|
|
other.keywordArgumentsMethods.end());
|
|
}
|
|
|
|
/// Returns true if type defines either of the @dynamicCallable
|
|
/// required methods. Returns false iff type does not satisfy @dynamicCallable
|
|
/// requirements.
|
|
bool isValid() const {
|
|
return !argumentsMethods.empty() || !keywordArgumentsMethods.empty();
|
|
}
|
|
};
|
|
|
|
/// Abstract base class for applying a solution to a SyntacticElementTarget.
|
|
class SyntacticElementTargetRewriter {
|
|
public:
|
|
virtual Solution &getSolution() const = 0;
|
|
virtual DeclContext *&getCurrentDC() const = 0;
|
|
|
|
virtual void addLocalDeclToTypeCheck(Decl *D) = 0;
|
|
|
|
[[nodiscard]]
|
|
virtual std::optional<SyntacticElementTarget>
|
|
rewriteTarget(SyntacticElementTarget target) = 0;
|
|
|
|
virtual ~SyntacticElementTargetRewriter() = default;
|
|
};
|
|
|
|
/// Retrieve the closure type from the constraint system.
|
|
struct GetClosureType {
|
|
ConstraintSystem &cs;
|
|
|
|
Type operator()(const AbstractClosureExpr *expr) const;
|
|
};
|
|
|
|
/// Retrieve the closure's preconcurrency status from the constraint system.
|
|
struct ClosureIsolatedByPreconcurrency {
|
|
ConstraintSystem &cs;
|
|
|
|
bool operator()(const ClosureExpr *expr) const;
|
|
};
|
|
|
|
/// Describes a system of constraints on type variables, the
|
|
/// solution of which assigns concrete types to each of the type variables.
|
|
/// Constraint systems are typically generated given an (untyped) expression.
|
|
class ConstraintSystem {
|
|
private:
|
|
ASTContext &Context;
|
|
SourceRange CurrentRange;
|
|
|
|
public:
|
|
DeclContext *DC;
|
|
ConstraintSystemOptions Options;
|
|
DiagnosticTransaction *diagnosticTransaction;
|
|
std::optional<ExpressionTimer> 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<Expr *, 2> 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;
|
|
|
|
/// The set of expressions for which we have generated constraints.
|
|
llvm::SetVector<Expr *> InputExprs;
|
|
|
|
/// The number of input expressions whose parents and depths have
|
|
/// been entered into \c ExprWeights.
|
|
unsigned NumInputExprsInWeights = 0;
|
|
|
|
llvm::DenseMap<Expr *, std::pair<unsigned, Expr *>> 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::pair<Type, DeclNameRef>, std::optional<LookupResult>>
|
|
MemberLookups;
|
|
|
|
/// Folding set containing all of the locators used in this
|
|
/// constraint system.
|
|
llvm::FoldingSetVector<ConstraintLocator> ConstraintLocators;
|
|
|
|
/// The overload sets that have been resolved along the current path.
|
|
llvm::DenseMap<ConstraintLocator *, SelectedOverload> ResolvedOverloads;
|
|
|
|
/// The current fixed score for this constraint system and the (partial)
|
|
/// solution it represents.
|
|
Score CurrentScore;
|
|
|
|
llvm::SetVector<TypeVariableType *> TypeVariables;
|
|
|
|
/// Maps expressions to types for choosing a favored overload
|
|
/// type in a disjunction constraint.
|
|
llvm::DenseMap<Expr *, TypeBase *> 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<const ClosureExpr *, FunctionType *> ClosureTypes;
|
|
|
|
/// Maps closures and local functions to the pack expansion expressions they
|
|
/// capture.
|
|
llvm::MapVector<AnyFunctionRef, SmallVector<PackExpansionExpr *, 1>> 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<Expr *, ImpliedResultKind> 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<AnyFunctionRef> 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<AnyFunctionRef, NominalTypeDecl *>,
|
|
std::pair<VarDecl *, BraceStmt *>>
|
|
BuilderTransformedBodies;
|
|
|
|
/// Arguments after the code completion token that were thus ignored (i.e.
|
|
/// assigned fresh type variables) for type checking.
|
|
llvm::SetVector<Expr *> 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<ASTNode, Type> 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<std::pair<const KeyPathExpr *, /*component index=*/unsigned>,
|
|
Type>
|
|
KeyPathComponentTypes;
|
|
|
|
/// Maps a key path root, value, and decl context to the key path expression.
|
|
llvm::DenseMap<const KeyPathExpr *,
|
|
std::tuple</*root=*/TypeVariableType *,
|
|
/*value=*/TypeVariableType *, DeclContext *>>
|
|
KeyPaths;
|
|
|
|
/// Maps AST entries to their targets.
|
|
llvm::DenseMap<SyntacticElementTargetKey, SyntacticElementTarget> 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<ASTNode, std::pair<ContextualTypeInfo, Type>> contextualTypes;
|
|
|
|
/// Information about each case label item tracked by the constraint system.
|
|
llvm::SmallDenseMap<const CaseLabelItem *, CaseLabelItemInfo, 4> 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<std::pair<CatchNode, PotentialThrowSite>> potentialThrowSites;
|
|
|
|
/// A map of expressions to the ExprPatterns that they are being solved as
|
|
/// a part of.
|
|
llvm::SmallDenseMap<Expr *, ExprPattern *, 2> exprPatterns;
|
|
|
|
/// The set of parameters that have been inferred to be 'isolated'.
|
|
llvm::SmallDenseSet<ParamDecl *, 2> isolatedParams;
|
|
|
|
/// The set of closures that have been inferred to be "isolated by
|
|
/// preconcurrency".
|
|
llvm::SmallDenseSet<const ClosureExpr *, 2> preconcurrencyClosures;
|
|
|
|
/// Maps closure parameters to type variables.
|
|
llvm::DenseMap<const ParamDecl *, TypeVariableType *>
|
|
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<std::pair<TypeBase *, TypeBase *>, ConversionRestrictionKind>
|
|
ConstraintRestrictions;
|
|
|
|
/// The set of fixes applied to make the solution work.
|
|
llvm::SmallSetVector<ConstraintFix *, 4> Fixes;
|
|
|
|
/// The set of remembered disjunction choices used to reach
|
|
/// the current constraint system.
|
|
llvm::SmallDenseMap<ConstraintLocator *, unsigned, 4> DisjunctionChoices;
|
|
|
|
/// A map from applied disjunction constraints to the corresponding
|
|
/// argument function type.
|
|
llvm::SmallDenseMap<ConstraintLocator *, FunctionType *, 4>
|
|
AppliedDisjunctions;
|
|
|
|
/// For locators associated with call expressions, the trailing closure
|
|
/// matching rule and parameter bindings that were applied.
|
|
llvm::DenseMap<ConstraintLocator *, MatchCallArgumentResult>
|
|
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<ConstraintLocator *, ArrayRef<OpenedType>, 4>
|
|
OpenedTypes;
|
|
|
|
/// A dictionary of all conformances that have been looked up by the solver.
|
|
llvm::DenseMap<std::pair<TypeBase *, ProtocolDecl *>, ProtocolConformanceRef>
|
|
Conformances;
|
|
|
|
/// A cache for unavailability checks peformed by the solver.
|
|
llvm::DenseMap<std::pair<const Decl *, ConstraintLocator *>, bool>
|
|
UnavailableDecls;
|
|
|
|
/// The list of all generic requirements fixed along the current
|
|
/// solver path.
|
|
using FixedRequirement =
|
|
std::tuple<GenericTypeParamType *, unsigned, TypeBase *>;
|
|
llvm::DenseSet<FixedRequirement> 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<ConstraintLocator *, ExistentialArchetypeType *, 4>
|
|
OpenedExistentialTypes;
|
|
|
|
llvm::SmallDenseMap<PackExpansionType *, TypeVariableType *, 4>
|
|
OpenedPackExpansionTypes;
|
|
|
|
llvm::SmallDenseMap<PackExpansionExpr *, GenericEnvironment *, 4>
|
|
PackExpansionEnvironments;
|
|
|
|
llvm::SmallDenseMap<PackElementExpr *, PackExpansionExpr *, 2>
|
|
PackElementExpansions;
|
|
|
|
/// The set of functions that have been transformed by a result builder.
|
|
llvm::MapVector<AnyFunctionRef, AppliedBuilderTransform>
|
|
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<ConstraintLocator *, ArgumentList *> ArgumentLists;
|
|
|
|
public:
|
|
/// A map from argument expressions to their applied property wrapper expressions.
|
|
llvm::SmallDenseMap<ASTNode, SmallVector<AppliedPropertyWrapper, 2>, 4>
|
|
appliedPropertyWrappers;
|
|
|
|
/// The locators of \c Defaultable constraints whose defaults were used.
|
|
llvm::DenseSet<ConstraintLocator *> 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<NominalTypeDecl *, DynamicCallableMethods>
|
|
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<ConstraintLocator *, UnresolvedDotExpr *, 2>
|
|
ImplicitCallAsFunctionRoots;
|
|
|
|
/// The set of conformances synthesized during solving (i.e. for
|
|
/// ad-hoc distributed `SerializationRequirement` conformances).
|
|
llvm::DenseMap<ConstraintLocator *, ProtocolDecl *>
|
|
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<OverloadSetRefExpr *, 4> &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<Solution> &solutions,
|
|
llvm::SmallSetVector<OverloadSetRefExpr *, 4> &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<OverloadSetRefExpr *, 4> &shrunkExprs) {
|
|
SmallVector<Constraint *, 8> disjunctions;
|
|
cs->collectDisjunctions(disjunctions);
|
|
|
|
unsigned unsolvedDisjunctions = disjunctions.size();
|
|
for (auto *disjunction : disjunctions) {
|
|
auto *locator = disjunction->getLocator();
|
|
if (!locator)
|
|
continue;
|
|
|
|
if (auto *OSR = getAsExpr<OverloadSetRefExpr>(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<Score> 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<Constraint *, 4> 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<Expr *> 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<KeyPathExpr>(expr))
|
|
for (auto i : indices(kp->getComponents()))
|
|
if (kp->getComponents()[i].getComponentType())
|
|
CS.cacheType(kp, i);
|
|
|
|
return Action::Continue(expr);
|
|
}
|
|
|
|
/// Ignore statements.
|
|
PreWalkResult<Stmt *> 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; }
|
|
|
|
/// 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<SelectedOverload>
|
|
findSelectedOverloadFor(ConstraintLocator *locator) const {
|
|
auto result = ResolvedOverloads.find(locator);
|
|
if (result == ResolvedOverloads.end())
|
|
return std::nullopt;
|
|
return result->second;
|
|
}
|
|
|
|
std::optional<SelectedOverload> 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<BraceStmt *>
|
|
swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func,
|
|
Type builderType);
|
|
|
|
friend std::optional<SyntacticElementTarget>
|
|
swift::TypeChecker::typeCheckExpression(
|
|
SyntacticElementTarget &target, OptionSet<TypeCheckExprFlags> options, DiagnosticTransaction *diagnosticTransaction);
|
|
|
|
friend std::optional<SyntacticElementTarget>
|
|
swift::TypeChecker::typeCheckTarget(
|
|
SyntacticElementTarget &target,
|
|
OptionSet<TypeCheckExprFlags> 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<Solution> &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<Type> getAlternativeLiteralTypes(KnownProtocolKind kind,
|
|
SmallVectorImpl<Type> &scratch);
|
|
|
|
/// Create a new type variable.
|
|
TypeVariableType *createTypeVariable(ConstraintLocator *locator,
|
|
unsigned options,
|
|
PreparedOverloadBuilder *preparedOverload
|
|
= nullptr);
|
|
|
|
/// Retrieve the set of active type variables.
|
|
ArrayRef<TypeVariableType *> 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<ImpliedResultKind> 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<ClosureExpr *>(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<PackExpansionExpr *, 1> getCapturedExpansions(AnyFunctionRef func) const {
|
|
auto result = CapturedExpansions.find(func);
|
|
if (result == CapturedExpansions.end())
|
|
return {};
|
|
|
|
return result->second;
|
|
}
|
|
|
|
void setCapturedExpansions(AnyFunctionRef func, SmallVector<PackExpansionExpr *, 1> exprs) {
|
|
assert(CapturedExpansions.count(func) == 0 && "Cannot reset captured expansions");
|
|
CapturedExpansions.insert({func, exprs});
|
|
}
|
|
|
|
TypeVariableType *getKeyPathValueType(const KeyPathExpr *keyPath) const {
|
|
auto result = getKeyPathValueTypeIfAvailable(keyPath);
|
|
assert(result);
|
|
return result;
|
|
}
|
|
|
|
TypeVariableType *
|
|
getKeyPathValueTypeIfAvailable(const KeyPathExpr *keyPath) const {
|
|
auto result = KeyPaths.find(keyPath);
|
|
if (result != KeyPaths.end())
|
|
return std::get<1>(result->second);
|
|
return nullptr;
|
|
}
|
|
|
|
TypeVariableType *getKeyPathRootType(const KeyPathExpr *keyPath) const {
|
|
auto result = getKeyPathRootTypeIfAvailable(keyPath);
|
|
assert(result);
|
|
return result;
|
|
}
|
|
|
|
TypeVariableType *
|
|
getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const {
|
|
auto result = KeyPaths.find(keyPath);
|
|
if (result != KeyPaths.end())
|
|
return std::get<0>(result->second);
|
|
return nullptr;
|
|
}
|
|
|
|
TypeBase *getFavoredType(Expr *E) {
|
|
assert(E != nullptr);
|
|
return this->FavoredTypes[E];
|
|
}
|
|
void setFavoredType(Expr *E, TypeBase *T) {
|
|
assert(E != nullptr);
|
|
this->FavoredTypes[E] = T;
|
|
}
|
|
|
|
/// Set the type in our type map for the given node, and record the change
|
|
/// in the trail.
|
|
///
|
|
/// The side tables are used through the expression type checker to avoid
|
|
/// mutating nodes until we know we have successfully type-checked them.
|
|
void setType(ASTNode node, Type type,
|
|
PreparedOverloadBuilder *preparedOverload=nullptr);
|
|
|
|
/// Undo the above change.
|
|
void restoreType(ASTNode node, Type oldType);
|
|
|
|
/// Check to see if we have a type for a node.
|
|
bool hasType(ASTNode node) const {
|
|
ASSERT(!node.isNull() && "Expected non-null node");
|
|
return NodeTypes.count(node) > 0;
|
|
}
|
|
|
|
/// Set the type in our type map for a given expression. The side
|
|
/// map is used throughout the expression type checker in order to
|
|
/// avoid mutating expressions until we know we have successfully
|
|
/// type-checked them.
|
|
void setType(const KeyPathExpr *KP, unsigned I, Type T);
|
|
|
|
void restoreType(const KeyPathExpr *KP, unsigned I, Type T);
|
|
|
|
bool hasType(const KeyPathExpr *KP, unsigned I) const {
|
|
ASSERT(KP && "Expected non-null key path parameter!");
|
|
return KeyPathComponentTypes.find(std::make_pair(KP, I))
|
|
!= KeyPathComponentTypes.end();
|
|
}
|
|
|
|
/// Get the type for an node.
|
|
Type getType(ASTNode node) const {
|
|
ASSERT(!node.isNull() && "Expected non-null node");
|
|
auto found = NodeTypes.find(node);
|
|
ASSERT(found != NodeTypes.end() && "Expected type to have been set!");
|
|
return found->second;
|
|
}
|
|
|
|
Type getType(const KeyPathExpr *KP, unsigned I) const {
|
|
ASSERT(KP && "Expected non-null key path parameter!");
|
|
auto found = KeyPathComponentTypes.find(std::make_pair(KP, I));
|
|
ASSERT(found != KeyPathComponentTypes.end() &&
|
|
"Expected type to have been set!");
|
|
return found->second;
|
|
}
|
|
|
|
/// Retrieve the type of the node, if known.
|
|
Type getTypeIfAvailable(ASTNode node) const {
|
|
auto known = NodeTypes.find(node);
|
|
if (known == NodeTypes.end())
|
|
return Type();
|
|
|
|
return known->second;
|
|
}
|
|
|
|
/// Cache the type of the expression argument and return that same
|
|
/// argument.
|
|
template <typename T>
|
|
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<ContextualTypeInfo> 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<SyntacticElementTarget>
|
|
getTargetFor(SyntacticElementTargetKey key) const;
|
|
|
|
std::optional<AppliedBuilderTransform>
|
|
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<VarDecl> builderSelf,
|
|
NullablePtr<BraceStmt> body) {
|
|
assert(builder->getAttrs().hasAttribute<ResultBuilderAttr>());
|
|
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<std::pair<VarDecl *, BraceStmt *>>
|
|
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<CaseLabelItem *>(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<CaseLabelItemInfo>
|
|
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<ConstraintLocator::PathElement> 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<Type, ExistentialArchetypeType *>
|
|
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<ConstraintLocator::PathElement> 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<ConstraintLocator::PathElement> newElts);
|
|
|
|
/// Retrieve the locator described by a given builder extended by an array of
|
|
/// path elements.
|
|
ConstraintLocator *
|
|
getConstraintLocator(const ConstraintLocatorBuilder &builder,
|
|
ArrayRef<ConstraintLocator::PathElement> newElts);
|
|
|
|
/// Retrieve the constraint locator described by the given
|
|
/// builder.
|
|
ConstraintLocator *
|
|
getConstraintLocator(const ConstraintLocatorBuilder &builder);
|
|
|
|
/// Lookup and return parent associated with given expression.
|
|
Expr *getParentExpr(Expr *expr) {
|
|
if (auto result = getExprDepthAndParent(expr))
|
|
return result->second;
|
|
return nullptr;
|
|
}
|
|
|
|
Expr *getSemanticsProvidingParentExpr(Expr *expr) {
|
|
while (auto *parent = getParentExpr(expr)) {
|
|
if (parent->getSemanticsProvidingExpr() == parent)
|
|
return parent;
|
|
expr = parent;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// Retrieve the depth of the given expression.
|
|
std::optional<unsigned> 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<std::pair<unsigned, Expr *>> 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<Type(Expr *)> getType,
|
|
llvm::function_ref<Type(Type)> simplifyType,
|
|
llvm::function_ref<std::optional<SelectedOverload>(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<SelectedOverload> {
|
|
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<ConstraintFix *> 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<FixKind> 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<Solution> solutions);
|
|
bool diagnoseAmbiguityWithFixes(SmallVectorImpl<Solution> &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> 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<std::pair<Type, ConstraintLocator *>> inputs,
|
|
std::optional<Type> supertype = std::nullopt) {
|
|
return addJoinConstraint<decltype(inputs)::iterator>(
|
|
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 <typename Iterator>
|
|
Type addJoinConstraint(
|
|
ConstraintLocator *locator, Iterator begin, Iterator end,
|
|
std::optional<Type> supertype,
|
|
std::function<std::pair<Type, ConstraintLocator *>(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<RawExprKind, TypeVariableType *> 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<TypeVariableType>()) {
|
|
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<TypeVariableType *> componentTypeVars,
|
|
ConstraintLocatorBuilder locator,
|
|
bool isFavored = false);
|
|
|
|
/// Add a new constraint with a restriction on its application.
|
|
void addRestrictedConstraint(ConstraintKind kind,
|
|
ConversionRestrictionKind restriction,
|
|
Type first, Type second,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Add a constraint that binds an overload set to a specific choice.
|
|
void addBindOverloadConstraint(Type boundTy, OverloadChoice choice,
|
|
ConstraintLocator *locator, DeclContext *useDC) {
|
|
resolveOverload(choice, useDC, locator, boundTy,
|
|
/*preparedOverload=*/nullptr);
|
|
}
|
|
|
|
/// Add a value member constraint to the constraint system.
|
|
void addValueMemberConstraint(Type baseTy, DeclNameRef name, Type memberTy,
|
|
DeclContext *useDC,
|
|
FunctionRefInfo functionRefInfo,
|
|
ArrayRef<OverloadChoice> 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<Constraint *> constraints,
|
|
ConstraintLocatorBuilder locator,
|
|
RememberChoice_t rememberChoice = ForgetChoice) {
|
|
auto constraint =
|
|
Constraint::createDisjunction(*this, constraints,
|
|
getConstraintLocator(locator),
|
|
rememberChoice);
|
|
|
|
addUnsolvedConstraint(constraint);
|
|
}
|
|
|
|
/// Whether we should record the failure of a constraint.
|
|
bool shouldRecordFailedConstraint() const {
|
|
// If we're debugging, always note a failure so we can print it out.
|
|
if (isDebugMode())
|
|
return true;
|
|
|
|
// Otherwise, only record it if we don't already have a failed constraint.
|
|
// This avoids allocating unnecessary constraints.
|
|
return !failedConstraint;
|
|
}
|
|
|
|
/// Note that a particular constraint has failed, setting \c failedConstraint
|
|
/// if necessary.
|
|
void recordFailedConstraint(Constraint *constraint) {
|
|
assert(!constraint->isActive());
|
|
if (!failedConstraint)
|
|
failedConstraint = constraint;
|
|
|
|
if (isDebugMode()) {
|
|
auto &log = llvm::errs();
|
|
log.indent(solverState ? solverState->getCurrentIndent() + 4 : 0)
|
|
<< "(failed constraint ";
|
|
constraint->print(log, &getASTContext().SourceMgr);
|
|
log << ")\n";
|
|
}
|
|
}
|
|
|
|
/// Remove a constraint from the system that has failed, setting
|
|
/// \c failedConstraint if necessary.
|
|
void retireFailedConstraint(Constraint *constraint) {
|
|
retireConstraint(constraint);
|
|
recordFailedConstraint(constraint);
|
|
}
|
|
|
|
/// Add a newly-generated constraint that is known not to be solvable
|
|
/// right now.
|
|
void addUnsolvedConstraint(Constraint *constraint) {
|
|
// We couldn't solve this constraint; add it to the pile.
|
|
InactiveConstraints.push_back(constraint);
|
|
|
|
// Add this constraint to the constraint graph.
|
|
CG.addConstraint(constraint);
|
|
|
|
if (isDebugMode() && solverState) {
|
|
auto &log = llvm::errs();
|
|
log.indent(solverState->getCurrentIndent() + 4) << "(added constraint: ";
|
|
constraint->print(log, &getASTContext().SourceMgr,
|
|
solverState->getCurrentIndent() + 4);
|
|
log << ")\n";
|
|
}
|
|
|
|
// Record this as a newly-generated constraint.
|
|
if (solverState)
|
|
solverState->addGeneratedConstraint(constraint);
|
|
}
|
|
|
|
/// Remove an inactive constraint from the current constraint graph.
|
|
void removeInactiveConstraint(Constraint *constraint) {
|
|
CG.removeConstraint(constraint);
|
|
|
|
auto where = InactiveConstraints.erase(constraint);
|
|
if (solverState)
|
|
recordChange(SolverTrail::Change::RetiredConstraint(where, constraint));
|
|
|
|
if (isDebugMode() && solverState) {
|
|
auto &log = llvm::errs();
|
|
log.indent(solverState->getCurrentIndent() + 4)
|
|
<< "(removed constraint: ";
|
|
constraint->print(log, &getASTContext().SourceMgr,
|
|
solverState->getCurrentIndent() + 4);
|
|
log << ")\n";
|
|
}
|
|
}
|
|
|
|
/// Transfer given constraint from to active list
|
|
/// for solver to attempt its simplification.
|
|
void activateConstraint(Constraint *constraint) {
|
|
assert(!constraint->isActive() && "Constraint is already active");
|
|
ActiveConstraints.splice(ActiveConstraints.end(), InactiveConstraints,
|
|
constraint);
|
|
constraint->setActive(true);
|
|
}
|
|
|
|
void deactivateConstraint(Constraint *constraint) {
|
|
assert(constraint->isActive() && "Constraint is already inactive");
|
|
InactiveConstraints.splice(InactiveConstraints.end(),
|
|
ActiveConstraints, constraint);
|
|
constraint->setActive(false);
|
|
}
|
|
|
|
void retireConstraint(Constraint *constraint) {
|
|
if (constraint->isActive())
|
|
deactivateConstraint(constraint);
|
|
removeInactiveConstraint(constraint);
|
|
}
|
|
|
|
/// Note that this constraint is "favored" within its disjunction, and
|
|
/// should be tried first to the exclusion of non-favored constraints in
|
|
/// the same disjunction.
|
|
void favorConstraint(Constraint *constraint) {
|
|
if (constraint->isFavored())
|
|
return;
|
|
|
|
if (solverState) {
|
|
solverState->favorConstraint(constraint);
|
|
} else {
|
|
constraint->setFavored();
|
|
}
|
|
}
|
|
|
|
/// Retrieve the list of inactive constraints.
|
|
ConstraintList &getConstraints() { return InactiveConstraints; }
|
|
|
|
/// The worklist of "active" constraints that should be revisited
|
|
/// due to a change.
|
|
ConstraintList &getActiveConstraints() { return ActiveConstraints; }
|
|
|
|
/// Retrieve the representative of the equivalence class containing
|
|
/// this type variable.
|
|
TypeVariableType *getRepresentative(TypeVariableType *typeVar) const {
|
|
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 the type of the projection if the decl has an associated property
|
|
/// wrapper with a projectedValue.
|
|
Type getPropertyWrapperProjectionType(SelectedOverload resolvedOverload);
|
|
|
|
/// Gets the type of the backing storage if the decl has an associated
|
|
/// property wrapper.
|
|
Type getPropertyWrapperBackingType(SelectedOverload resolvedOverload);
|
|
|
|
/// Gets the type of a wrapped property if resolved overload has
|
|
/// a decl which is the backing storage for a property wrapper.
|
|
Type getWrappedPropertyType(SelectedOverload resolvedOverload);
|
|
|
|
/// Merge the equivalence sets of the two type variables.
|
|
///
|
|
/// Note that both \c typeVar1 and \c typeVar2 must be the
|
|
/// representatives of their equivalence classes, and must be
|
|
/// distinct.
|
|
void mergeEquivalenceClasses(TypeVariableType *typeVar1,
|
|
TypeVariableType *typeVar2,
|
|
bool updateWorkList);
|
|
|
|
/// Flags that direct type matching.
|
|
enum TypeMatchFlags {
|
|
/// Indicates that we are in a context where we should be
|
|
/// generating constraints for any unsolvable problems.
|
|
///
|
|
/// This flag is automatically introduced when type matching destructures
|
|
/// a type constructor (tuple, function type, etc.), solving that
|
|
/// constraint while potentially generating others.
|
|
TMF_GenerateConstraints = 0x01,
|
|
|
|
/// Indicates that we are applying a fix.
|
|
TMF_ApplyingFix = 0x02,
|
|
|
|
/// Indicates that we are attempting a possible type for
|
|
/// a type variable.
|
|
TMF_BindingTypeVariable = 0x04,
|
|
|
|
/// Indicates that the solver is matching one of the
|
|
/// generic argument pairs as part of matching two generic types.
|
|
TMF_MatchingGenericArguments = 0x08,
|
|
};
|
|
|
|
/// Options that govern how type matching should proceed.
|
|
using TypeMatchOptions = OptionSet<TypeMatchFlags>;
|
|
|
|
/// 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<std::pair<Type, Type>> isDictionaryType(Type type);
|
|
|
|
/// Determine if the type in question is a Set<T> and, if so, provide the
|
|
/// element type of the set.
|
|
static std::optional<Type> 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<OpenedType> 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<OpenedType> 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<OpenedType> &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<OpenedType> &replacements,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
/// Open the generic parameter list creating type variables for each of the
|
|
/// type parameters.
|
|
void openGenericParameters(DeclContext *outerDC,
|
|
GenericSignature signature,
|
|
SmallVectorImpl<OpenedType> &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<Type(Type)> 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<Type(Type)> subst,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
/// Update OpenedTypes and record a change in the trail.
|
|
void recordOpenedType(
|
|
ConstraintLocator *locator, ArrayRef<OpenedType> openedTypes,
|
|
PreparedOverloadBuilder *preparedOverload = nullptr);
|
|
|
|
/// Record the set of opened types for the given locator.
|
|
void recordOpenedTypes(
|
|
ConstraintLocatorBuilder locator,
|
|
const SmallVectorImpl<OpenedType> &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,
|
|
bool openGlobalActorType, ConstraintLocatorBuilder locator);
|
|
|
|
/// Retrieve the type of a reference to the given value declaration.
|
|
///
|
|
/// For references to polymorphic function types, this routine "opens up"
|
|
/// the type by replacing each instance of an archetype with a fresh type
|
|
/// variable.
|
|
///
|
|
/// \returns a description of the type of this declaration reference.
|
|
DeclReferenceType getTypeOfReference(
|
|
OverloadChoice choice, DeclContext *useDC, ConstraintLocatorBuilder locator,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
/// Retrieve the type of a reference to the given value declaration,
|
|
/// as a member with a base of the given type.
|
|
///
|
|
/// For references to generic function types or members of generic types,
|
|
/// this routine "opens up" the type by replacing each instance of a generic
|
|
/// parameter with a fresh type variable.
|
|
///
|
|
/// \returns a description of the type of this declaration reference.
|
|
DeclReferenceType getTypeOfMemberReference(
|
|
OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
/// Retrieve a list of generic parameter types solver has "opened" (replaced
|
|
/// with a type variable) at the given location.
|
|
ArrayRef<OpenedType> getOpenedTypes(ConstraintLocator *locator) const {
|
|
auto substitutions = OpenedTypes.find(locator);
|
|
if (substitutions == OpenedTypes.end())
|
|
return {};
|
|
return substitutions->second;
|
|
}
|
|
|
|
private:
|
|
/// \returns The opened type and the thrown error type.
|
|
std::pair<Type, Type> getTypeOfReferencePre(
|
|
OverloadChoice choice, DeclContext *useDC, ConstraintLocatorBuilder locator,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
DeclReferenceType getTypeOfReferencePost(
|
|
OverloadChoice choice, DeclContext *useDC, ConstraintLocatorBuilder locator,
|
|
Type openedType, Type thrownErrorType);
|
|
|
|
/// \returns the opened type and the thrown error type.
|
|
std::pair<Type, Type> getTypeOfMemberReferencePre(
|
|
OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
DeclReferenceType getTypeOfMemberReferencePost(
|
|
OverloadChoice choice, DeclContext *useDC, ConstraintLocator *locator,
|
|
Type openedType, Type thrownErrorType);
|
|
|
|
Type getTypeOfMemberTypeReference(
|
|
Type baseObjTy, TypeDecl *typeDecl, ConstraintLocator *locator,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
/// Return the type-of-reference of the given value.
|
|
///
|
|
/// \param baseType if non-null, return the type of a member reference to
|
|
/// this value when the base has the given type
|
|
///
|
|
/// \param UseDC The context of the access. Some variables have different
|
|
/// types depending on where they are used.
|
|
///
|
|
/// \param locator The locator anchored at this value reference, when
|
|
/// it is a member reference.
|
|
///
|
|
/// \param wantInterfaceType Whether we want the interface type, if available.
|
|
Type getUnopenedTypeOfReference(VarDecl *value, Type baseType,
|
|
DeclContext *UseDC,
|
|
ConstraintLocator *locator,
|
|
bool wantInterfaceType);
|
|
|
|
std::pair<Type, Type> getOpenedStorageType(
|
|
Type baseTy, AbstractStorageDecl *value, DeclContext *useDC,
|
|
bool hasAppliedSelf, ArrayRef<OpenedType> replacements,
|
|
ConstraintLocator *locator, PreparedOverloadBuilder *preparedOverload);
|
|
|
|
/// Given the opened type and a pile of information about a member reference,
|
|
/// determine the reference type of the member reference.
|
|
Type getMemberReferenceTypeFromOpenedType(
|
|
Type type, Type baseObjTy, ValueDecl *value,
|
|
ConstraintLocator *locator, bool hasAppliedSelf, bool isDynamicLookup);
|
|
|
|
/// Add the constraints needed to bind an overload's type variable.
|
|
void bindOverloadType(const SelectedOverload &overload, Type boundType,
|
|
ConstraintLocator *locator, DeclContext *useDC);
|
|
|
|
/// Describes a direction of optional wrapping, either increasing optionality
|
|
/// or decreasing optionality.
|
|
enum class OptionalWrappingDirection {
|
|
/// Unwrap an optional type T? to T.
|
|
Unwrap,
|
|
|
|
/// Promote a type T to optional type T?.
|
|
Promote
|
|
};
|
|
|
|
/// Attempts to find a constraint that involves \p typeVar and satisfies
|
|
/// \p predicate, looking through optional object constraints if necessary. If
|
|
/// multiple candidates are found, returns the first one.
|
|
///
|
|
/// \param optionalDirection The direction to travel through optional object
|
|
/// constraints, either increasing or decreasing optionality.
|
|
///
|
|
/// \param predicate Checks whether a given constraint is the one being
|
|
/// searched for. The type variable passed is the current representative
|
|
/// after looking through the optional object constraints.
|
|
///
|
|
/// \returns The constraint found along with the number of optional object
|
|
/// constraints looked through, or \c None if no constraint was found.
|
|
std::optional<std::pair<Constraint *, unsigned>>
|
|
findConstraintThroughOptionals(
|
|
TypeVariableType *typeVar, OptionalWrappingDirection optionalDirection,
|
|
llvm::function_ref<bool(Constraint *, TypeVariableType *)> 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<OverloadChoice> choices,
|
|
DeclContext *useDC, ConstraintLocator *locator,
|
|
std::optional<unsigned> favoredIndex = std::nullopt);
|
|
|
|
void addOverloadSet(ArrayRef<Constraint *> choices,
|
|
ConstraintLocator *locator);
|
|
|
|
/// Retrieve the allocator used by this constraint system.
|
|
llvm::BumpPtrAllocator &getAllocator() { return Allocator; }
|
|
|
|
template <typename It>
|
|
ArrayRef<typename std::iterator_traits<It>::value_type>
|
|
allocateCopy(It start, It end) {
|
|
using T = typename std::iterator_traits<It>::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<T>(result, i);
|
|
}
|
|
|
|
template<typename T>
|
|
ArrayRef<T> allocateCopy(ArrayRef<T> array) {
|
|
return allocateCopy(array.begin(), array.end());
|
|
}
|
|
|
|
template<typename T>
|
|
ArrayRef<T> allocateCopy(SmallVectorImpl<T> &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<ExprPattern *> 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<Constraint *> &constraints, Type type,
|
|
ArrayRef<OverloadChoice> choices, DeclContext *useDC,
|
|
ConstraintLocator *locator,
|
|
std::optional<unsigned> favoredIndex = std::nullopt,
|
|
bool requiresFix = false,
|
|
llvm::function_ref<ConstraintFix *(unsigned, const OverloadChoice &)>
|
|
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<RestrictionOrFix> &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<TypeMatchResult()> 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(OverloadChoice choice,
|
|
DeclContext *useDC,
|
|
ConstraintLocator *locator,
|
|
bool forDiagnostics);
|
|
|
|
/// Populate the prepared overload with all type variables and constraints
|
|
/// that are to be introduced into the constraint system when this choice
|
|
/// is taken.
|
|
///
|
|
/// Returns a pair consisting of the opened type, and the thrown error type.
|
|
///
|
|
/// FIXME: As a transitional mechanism, if preparedOverload is nullptr, this
|
|
/// immediately performs all operations.
|
|
std::pair<Type, Type>
|
|
prepareOverloadImpl(OverloadChoice choice,
|
|
DeclContext *useDC,
|
|
ConstraintLocator *locator,
|
|
PreparedOverloadBuilder *preparedOverload);
|
|
|
|
void replayChanges(
|
|
ConstraintLocator *locator,
|
|
PreparedOverload *preparedOverload);
|
|
|
|
/// Resolve the given overload set to the given choice.
|
|
void resolveOverload(OverloadChoice choice, DeclContext *useDC,
|
|
ConstraintLocator *locator, Type boundType,
|
|
PreparedOverload *preparedOverload);
|
|
|
|
/// Simplify a type, by replacing type variables with either their
|
|
/// fixed types (if available) or their representatives.
|
|
///
|
|
/// The resulting types can be compared canonically, so long as additional
|
|
/// type equivalence requirements aren't introduced between comparisons.
|
|
Type simplifyType(Type type);
|
|
|
|
/// Simplify a type, by replacing type variables with either their
|
|
/// fixed types (if available) or their representatives.
|
|
///
|
|
/// \param flags If the simplified type has changed, this will be updated
|
|
/// to include \c TMF_GenerateConstraints.
|
|
///
|
|
/// The resulting types can be compared canonically, so long as additional
|
|
/// type equivalence requirements aren't introduced between comparisons.
|
|
Type simplifyType(Type type, TypeMatchOptions &flags) {
|
|
Type result = simplifyType(type);
|
|
if (result.getPointer() != type.getPointer())
|
|
flags |= TMF_GenerateConstraints;
|
|
return result;
|
|
}
|
|
|
|
/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
|
|
/// perform a lookup into the specified base type to find a candidate list.
|
|
/// The list returned includes the viable candidates as well as the unviable
|
|
/// ones (along with reasons why they aren't viable).
|
|
///
|
|
/// If includeInaccessibleMembers is set to true, this burns compile time to
|
|
/// try to identify and classify inaccessible members that may be being
|
|
/// referenced.
|
|
MemberLookupResult performMemberLookup(ConstraintKind constraintKind,
|
|
DeclNameRef memberName, Type baseTy,
|
|
FunctionRefInfo functionRefInfo,
|
|
ConstraintLocator *memberLocator,
|
|
bool includeInaccessibleMembers);
|
|
|
|
/// Build implicit autoclosure expression wrapping a given expression.
|
|
/// Given expression represents computed result of the closure.
|
|
///
|
|
/// The \p ClosureDC must be the deepest possible context that
|
|
/// contains this autoclosure expression. For example,
|
|
///
|
|
/// func foo() {
|
|
/// _ = { $0 || $1 || $2 }
|
|
/// }
|
|
///
|
|
/// Even though the decl context of $1 (after solution application) is
|
|
/// `||`'s autoclosure parameter, we cannot know this until solution
|
|
/// application has finished because autoclosure expressions are expanded in
|
|
/// depth-first order then \c ContextualizeClosures comes around to clean up.
|
|
/// All that is required is that the explicit closure be the context since it
|
|
/// is the innermost context that can introduce potential new capturable
|
|
/// declarations.
|
|
Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType,
|
|
DeclContext *ClosureDC,
|
|
bool isDefaultWrappedValue = false,
|
|
bool isAsyncLetWrapper = false);
|
|
|
|
/// Builds a type-erased return expression that can be used in dynamic
|
|
/// replacement.
|
|
///
|
|
/// An expression needs type erasure if:
|
|
/// 1. The expression is a return value.
|
|
/// 2. The enclosing function is dynamic, a dynamic replacement, or
|
|
/// `-enable-experimental-opaque-type-erasure` is used.
|
|
/// 3. The enclosing function returns an opaque type.
|
|
/// 4. The opaque type conforms to (exactly) one protocol, and the protocol
|
|
/// has a declared type eraser.
|
|
///
|
|
/// \returns the transformed return expression, or the original expression if
|
|
/// no type erasure is needed.
|
|
Expr *buildTypeErasedExpr(Expr *expr, DeclContext *dc, Type contextualType,
|
|
ContextualTypePurpose purpose);
|
|
|
|
/// Ensures that the given argument type conforms to the `Hashable` protocol
|
|
/// and adds a conformance constraint if it does not. This is required for
|
|
/// arguments used as key path components, as they serve as lookup keys.
|
|
void verifyThatArgumentIsHashable(unsigned index, Type argType,
|
|
ConstraintLocator *locator, SourceLoc loc);
|
|
|
|
private:
|
|
/// Determines whether or not a given conversion at a given locator requires
|
|
/// the creation of a temporary value that's only valid for a limited scope.
|
|
/// Such ephemeral conversions, such as array-to-pointer, cannot be passed to
|
|
/// non-ephemeral parameters.
|
|
ConversionEphemeralness
|
|
isConversionEphemeral(ConversionRestrictionKind conversion,
|
|
ConstraintLocatorBuilder locator);
|
|
|
|
/// Simplifies a type by replacing type variables with the result of
|
|
/// \c getFixedTypeFn and performing lookup on dependent member types.
|
|
Type
|
|
simplifyTypeImpl(Type type,
|
|
llvm::function_ref<Type(TypeVariableType *)> 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<OverloadChoice> 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> 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<TypeVariableType *> 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<TypeVariableType *> 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<TypeMatchResult>
|
|
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<BindingSet> determineBestBindings(
|
|
llvm::function_ref<void(const BindingSet &)> 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<Constraint *> &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<bool(Constraint *)> 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<Solution> &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<std::pair<Constraint *, llvm::TinyPtrVector<Constraint *>>>
|
|
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();
|
|
|
|
void diagnoseTooComplex(SourceLoc fallbackLoc,
|
|
SolutionResult &result);
|
|
|
|
/// Solve the system of constraints generated from provided expression.
|
|
///
|
|
/// \param target The target to generate constraints from.
|
|
/// \param allowFreeTypeVariables How to bind free type variables in
|
|
/// the solution.
|
|
SolutionResult solveImpl(SyntacticElementTarget &target,
|
|
FreeTypeVariableBinding allowFreeTypeVariables =
|
|
FreeTypeVariableBinding::Disallow);
|
|
|
|
public:
|
|
/// Pre-check the target, validating any types that occur in it
|
|
/// and folding sequence expressions.
|
|
static bool preCheckTarget(SyntacticElementTarget &target);
|
|
|
|
/// Solve the system of constraints generated from provided target.
|
|
///
|
|
/// \param target The target that we'll generate constraints from, which
|
|
/// may be updated by the solving process.
|
|
/// \param allowFreeTypeVariables How to bind free type variables in
|
|
/// the solution.
|
|
///
|
|
/// \returns the set of solutions, if any were found, or \c None if an
|
|
/// error occurred. When \c None, an error has been emitted.
|
|
std::optional<std::vector<Solution>>
|
|
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<Solution> &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<Solution>
|
|
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<Solution> &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<Solution> &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<Solution> &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<Solution> solutions,
|
|
const SolutionDiff &diff, unsigned idx1, unsigned idx2);
|
|
|
|
void startExpressionTimer();
|
|
|
|
public:
|
|
/// Increase the score of the given kind for the current (partial) solution
|
|
/// along the current solver path.
|
|
void increaseScore(ScoreKind kind, ConstraintLocatorBuilder Locator,
|
|
unsigned value = 1);
|
|
|
|
/// Primitive form of the above. Records a change in the trail.
|
|
void increaseScore(ScoreKind kind, unsigned value);
|
|
|
|
/// Apply the score from a partial solution. Records the change in the
|
|
/// trail.
|
|
void replayScore(const Score &score);
|
|
|
|
/// Temporarily zero out the score, and record this in the trail so that
|
|
/// we restore the score when the scope ends. Used when solving a
|
|
/// ConjunctionStep.
|
|
void clearScore();
|
|
|
|
/// Determine whether this solution is guaranteed to be worse than the best
|
|
/// solution found so far.
|
|
bool worseThanBestSolution() const;
|
|
|
|
/// Given a set of viable solutions, find the best
|
|
/// solution.
|
|
///
|
|
/// \param solutions The set of viable solutions to consider.
|
|
///
|
|
/// \param minimize If true, then in the case where there is no single
|
|
/// best solution, minimize the set of solutions by removing any solutions
|
|
/// that are identical to or worse than some other solution. This operation
|
|
/// is quadratic.
|
|
///
|
|
/// \returns The index of the best solution, or nothing if there was no
|
|
/// best solution.
|
|
std::optional<unsigned> findBestSolution(SmallVectorImpl<Solution> &solutions,
|
|
bool minimize);
|
|
|
|
/// Apply a given solution to the target, producing a fully
|
|
/// type-checked target or \c None if an error occurred.
|
|
///
|
|
/// \param target the target to which the solution will be applied.
|
|
std::optional<SyntacticElementTarget>
|
|
applySolution(Solution &solution, SyntacticElementTarget target);
|
|
|
|
/// Apply the given solution to the given statement-condition.
|
|
std::optional<StmtCondition>
|
|
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);
|
|
|
|
/// Set the current sub-expression (of a multi-statement closure, etc) for
|
|
/// the purposes of diagnosing "reasonable time" errors.
|
|
void startExpression(ASTNode node);
|
|
|
|
/// The source range of the target being type checked.
|
|
SourceRange getCurrentSourceRange() const {
|
|
return CurrentRange;
|
|
}
|
|
|
|
/// Determine if we've already explored too many paths in an
|
|
/// attempt to solve this expression.
|
|
std::pair<bool, SourceRange> isAlreadyTooComplex = {false, SourceRange()};
|
|
|
|
/// If optional is not nil, result is guaranteed to point at a valid
|
|
/// location.
|
|
std::optional<SourceRange> getTooComplexRange() const {
|
|
auto range = isAlreadyTooComplex.second;
|
|
return range.isValid() ? range : std::optional<SourceRange>();
|
|
}
|
|
|
|
bool isTooComplex(size_t solutionMemory);
|
|
|
|
bool isTooComplex(ArrayRef<Solution> 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<ConstraintLocator *, SelectedOverload> &
|
|
getResolvedOverloads() const {
|
|
return ResolvedOverloads;
|
|
}
|
|
|
|
/// If we aren't certain that we've emitted a diagnostic, emit a fallback
|
|
/// diagnostic.
|
|
void maybeProduceFallbackDiagnostic(SourceLoc loc) const;
|
|
|
|
/// Check whether given AST node represents an argument of an application
|
|
/// of some sort (call, operator invocation, subscript etc.)
|
|
/// and returns a locator for the argument application. E.g. for regular
|
|
/// calls `test(42)` passing `42` should return a locator with the entire call
|
|
/// as the anchor, and a path to the argument at index `0`.
|
|
ConstraintLocator *getArgumentLocator(Expr *expr);
|
|
|
|
/// Determine whether given locator represents an argument to declaration
|
|
/// imported from C/ObjectiveC.
|
|
bool isArgumentOfImportedDecl(ConstraintLocatorBuilder locator);
|
|
|
|
/// Visit each subexpression that will be part of the constraint system
|
|
/// of the given expression, including those in closure bodies that will be
|
|
/// part of the constraint system.
|
|
void forEachExpr(Expr *expr, llvm::function_ref<Expr *(Expr *)> 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</*isValid=*/bool, std::optional<KeyPathCapability>>
|
|
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</*isValid=*/bool, std::optional<KeyPathCapability>>
|
|
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);
|
|
}
|
|
};
|
|
|
|
/// A function object suitable for use as an \c OpenRequirementFn that "opens"
|
|
/// the requirements for a given type's generic signature given a set of
|
|
/// argument substitutions.
|
|
class OpenGenericTypeRequirements {
|
|
ConstraintSystem &cs;
|
|
const ConstraintLocatorBuilder &locator;
|
|
PreparedOverloadBuilder *preparedOverload;
|
|
|
|
public:
|
|
explicit OpenGenericTypeRequirements(
|
|
ConstraintSystem &cs, const ConstraintLocatorBuilder &locator,
|
|
PreparedOverloadBuilder *preparedOverload)
|
|
: cs(cs), locator(locator), preparedOverload(preparedOverload) {}
|
|
|
|
void operator()(GenericTypeDecl *decl, TypeSubstitutionFn subst) const;
|
|
};
|
|
|
|
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<unsigned> &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<unsigned> 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<ParamBinding> 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<Identifier> 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<unsigned> 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<CompletionArgInfo> 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<MatchCallArgumentResult> matchCallArguments(
|
|
SmallVectorImpl<AnyFunctionType::Param> &args,
|
|
ArrayRef<AnyFunctionType::Param> params, const ParameterListInfo ¶mInfo,
|
|
std::optional<unsigned> unlabeledTrailingClosureIndex, bool allowFixes,
|
|
MatchCallArgumentListener &listener,
|
|
std::optional<TrailingClosureMatching> 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<LocatorPathElt> &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<Identifier> 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<Type(Type)> 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<DisfavoredOverloadAttr>();
|
|
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<TypeVariableType *> &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<std::pair<ConstraintFix *, unsigned>>
|
|
fixForHole(ConstraintSystem &cs) const;
|
|
|
|
void print(llvm::raw_ostream &Out, SourceManager *, unsigned indent) const {
|
|
PrintOptions PO = PrintOptions::forDebugging();
|
|
Out << "type variable binding " << TypeVar->getString(PO)
|
|
<< " := " << Binding.BindingType->getString(PO);
|
|
}
|
|
};
|
|
|
|
template<typename Choice>
|
|
class BindingProducer {
|
|
ConstraintLocator *Locator;
|
|
|
|
protected:
|
|
ConstraintSystem &CS;
|
|
|
|
public:
|
|
BindingProducer(ConstraintSystem &cs, ConstraintLocator *locator)
|
|
: Locator(locator), CS(cs) {}
|
|
|
|
virtual ~BindingProducer() {}
|
|
virtual std::optional<Choice> 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<TypeVariableBinding> {
|
|
using BindingKind = AllowedBindingKind;
|
|
using Binding = PotentialBinding;
|
|
|
|
TypeVariableType *TypeVar;
|
|
llvm::SmallVector<Binding, 8> Bindings;
|
|
/// The set of defaults to attempt once producer
|
|
/// runs out of direct & transitive bindings.
|
|
llvm::SmallVector<Constraint *, 4> 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<CanType, 4> ExploredTypes;
|
|
llvm::SmallPtrSet<TypeBase *, 4> 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<Binding> getCurrentBindings() const { return Bindings; }
|
|
|
|
std::optional<Element> 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<DisjunctionChoice> {
|
|
// The disjunction choices that this producer will iterate through.
|
|
ArrayRef<Constraint *> 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<unsigned, 8> 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<unsigned, 4> 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<Constraint *> &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<Element> 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<unsigned> &Ordering,
|
|
SmallVectorImpl<unsigned> &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<unsigned>::iterator first,
|
|
SmallVectorImpl<unsigned>::iterator last);
|
|
};
|
|
|
|
class ConjunctionElementProducer : public BindingProducer<ConjunctionElement> {
|
|
ArrayRef<Constraint *> 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<Element> 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<TypeVariableType *, 4> 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<Expr *> walkToExprPre(Expr *expr) override;
|
|
PostWalkResult<Expr *> walkToExprPost(Expr *expr) override;
|
|
PreWalkResult<Stmt *> 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<PatternBindingDecl>(D));
|
|
}
|
|
|
|
ArrayRef<TypeVariableType *> getTypeVars() const {
|
|
return TypeVars.getArrayRef();
|
|
}
|
|
};
|
|
|
|
/// Determine whether the given type is a PartialKeyPath and
|
|
/// AnyKeyPath or existential type thererof, for example,
|
|
/// `PartialKeyPath<...> & Sendable`.
|
|
bool isTypeErasedKeyPathType(Type type);
|
|
|
|
/// Determine whether given declaration is one for a key path
|
|
/// `{Writable, ReferenceWritable}KeyPath`.
|
|
bool isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl);
|
|
|
|
/// Determine whether given closure has any `return`
|
|
/// statements that could produce non-void result.
|
|
bool hasResultExpr(ClosureExpr *closure);
|
|
|
|
/// Emit diagnostics for syntactic restrictions within a given solution
|
|
/// application target.
|
|
void performSyntacticDiagnosticsForTarget(const SyntacticElementTarget &target,
|
|
bool isExprStmt);
|
|
|
|
/// Given a member of a protocol, check whether `Self` type of that
|
|
/// protocol is contextually bound to some concrete type via same-type
|
|
/// generic requirement and if so return that type or null type otherwise.
|
|
Type getConcreteReplacementForProtocolSelfType(ValueDecl *member);
|
|
|
|
/// Determine whether given disjunction constraint represents a set
|
|
/// of operator overload choices.
|
|
bool isOperatorDisjunction(Constraint *disjunction);
|
|
|
|
/// Find out whether closure body has any `async` or `await` expressions,
|
|
/// declarations, or statements directly in its body (no in other closures
|
|
/// or nested declarations).
|
|
ASTNode findAsyncNode(ClosureExpr *closure);
|
|
|
|
/// Check whether the given binding represents a placeholder variable that
|
|
/// has to get its type inferred at a first use site.
|
|
///
|
|
/// \returns The currently assigned type if it's a placeholder,
|
|
/// empty type otherwise.
|
|
Type isPlaceholderVar(PatternBindingDecl *PB);
|
|
|
|
/// Dump an anchor node for a constraint locator or contextual type.
|
|
void dumpAnchor(ASTNode anchor, SourceManager *SM, raw_ostream &out);
|
|
|
|
bool isPackExpansionType(Type type);
|
|
|
|
/// Check whether the type is a tuple consisting of a single unlabeled element
|
|
/// of \c PackExpansionType or a type variable that represents a pack expansion
|
|
/// type.
|
|
bool isSingleUnlabeledPackExpansionTuple(Type type);
|
|
|
|
bool containsPackExpansionType(ArrayRef<AnyFunctionType::Param> 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<typename ...Args>
|
|
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>(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<Expr *> walkToExprPre(Expr *expr) override {
|
|
if (auto applyExpr = dyn_cast<ApplyExpr>(expr)) {
|
|
// If we've found function application and it's
|
|
// function is an overload set, count it.
|
|
if (isa<OverloadSetRefExpr>(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<AnyFunctionType::Param> 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<AnyFunctionType::Param> 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<bool(unsigned inputIdx, unsigned paramIdx)> pairMatcher);
|
|
|
|
size_t getNumSkippedParameters() const { return NumSkippedParameters; }
|
|
};
|
|
|
|
// Return true if, when replacing "<expr>" with "<expr> ?? T", parentheses need
|
|
// to be added around <expr> first in order to maintain the correct precedence.
|
|
bool exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC,
|
|
Expr *expr);
|
|
|
|
// Return true if, when replacing "<expr>" with "<expr> 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<Expr *(const Expr *)> getParent);
|
|
|
|
/// Return true if, when replacing "<expr>" with "<expr> op <something>",
|
|
/// parentheses must be added around "<expr>" to allow the new operator
|
|
/// to bind correctly.
|
|
bool exprNeedsParensInsideFollowingOperator(DeclContext *DC,
|
|
Expr *expr,
|
|
PrecedenceGroupDecl *followingPG);
|
|
|
|
/// Return true if, when replacing "<expr>" with "<expr> op <something>",
|
|
/// 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<Expr *(const Expr *)> 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<swift::constraints::SyntacticElementTargetKey> {
|
|
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
|