blob: f2d9ae067dca0b98762499a9767d9e75b452874e [file] [log] [blame]
//===--- 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/DiagnosticsSema.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/Constraint.h"
#include "swift/Sema/ConstraintGraph.h"
#include "swift/Sema/ConstraintGraphScope.h"
#include "swift/Sema/ConstraintLocator.h"
#include "swift/Sema/CSFix.h"
#include "swift/Sema/OverloadChoice.h"
#include "swift/Sema/SolutionResult.h"
#include "llvm/ADT/MapVector.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>
namespace swift {
class Expr;
class FuncDecl;
class BraseStmt;
enum class TypeCheckExprFlags;
namespace constraints {
class ConstraintGraph;
class ConstraintGraphNode;
class ConstraintSystem;
class SolutionApplicationTarget;
} // end namespace constraints
namespace unittest {
class SemaTest;
} // end namespace unittest
// Forward declare some TypeChecker related functions
// so they could be made friends of ConstraintSystem.
namespace TypeChecker {
Optional<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,
Type builderType);
Optional<constraints::SolutionApplicationTarget>
typeCheckExpression(constraints::SolutionApplicationTarget &target,
OptionSet<TypeCheckExprFlags> options);
} // 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 {
/// This specifies the purpose of the contextual type, when specified to
/// typeCheckExpression. This is used for diagnostic generation to produce more
/// specified error messages when the conversion fails.
///
enum ContextualTypePurpose {
CTP_Unused, ///< No contextual type is specified.
CTP_Initialization, ///< Pattern binding initialization.
CTP_ReturnStmt, ///< Value specified to a 'return' statement.
CTP_ReturnSingleExpr, ///< Value implicitly returned from a function.
CTP_YieldByValue, ///< By-value yield operand.
CTP_YieldByReference, ///< By-reference yield operand.
CTP_ThrowStmt, ///< Value specified to a 'throw' statement.
CTP_EnumCaseRawValue, ///< Raw value specified for "case X = 42" in enum.
CTP_DefaultParameter, ///< Default value in parameter 'foo(a : Int = 42)'.
/// Default value in @autoclosure parameter
/// 'foo(a : @autoclosure () -> Int = 42)'.
CTP_AutoclosureDefaultParameter,
CTP_CalleeResult, ///< Constraint is placed on the result of a callee.
CTP_CallArgument, ///< Call to function or operator requires type.
CTP_ClosureResult, ///< Closure result expects a specific type.
CTP_ArrayElement, ///< ArrayExpr wants elements to have a specific type.
CTP_DictionaryKey, ///< DictionaryExpr keys should have a specific type.
CTP_DictionaryValue, ///< DictionaryExpr values should have a specific type.
CTP_CoerceOperand, ///< CoerceExpr operand coerced to specific type.
CTP_AssignSource, ///< AssignExpr source operand coerced to result type.
CTP_SubscriptAssignSource, ///< AssignExpr source operand coerced to subscript
///< result type.
CTP_Condition, ///< Condition expression of various statements e.g.
///< `if`, `for`, `while` etc.
CTP_ForEachStmt, ///< "expression/sequence" associated with 'for-in' loop
///< is expected to conform to 'Sequence' protocol.
CTP_WrappedProperty, ///< Property type expected to match 'wrappedValue' type
CTP_ComposedPropertyWrapper, ///< Composed wrapper type expected to match
///< former 'wrappedValue' type
CTP_CannotFail, ///< Conversion can never fail. abort() if it does.
};
/// 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,
/// Bind the type variables to UnresolvedType to represent the ambiguity.
UnresolvedType
};
namespace constraints {
/// 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,
};
/// A handle that holds the saved state of a type variable, which
/// can be restored.
class SavedTypeVariableBinding {
/// The type variable that we saved the state of.
TypeVariableType *TypeVar;
/// The saved type variable options.
unsigned Options;
/// The parent or fixed type.
llvm::PointerUnion<TypeVariableType *, TypeBase *> ParentOrFixed;
public:
explicit SavedTypeVariableBinding(TypeVariableType *typeVar);
/// Restore the state of the type variable to the saved state.
void restore();
};
/// A set of saved type variable bindings.
using SavedTypeVariableBindings = SmallVector<SavedTypeVariableBinding, 16>;
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) {}
Optional<ConversionRestrictionKind> getRestriction() const {
if (IsRestriction)
return Restriction;
return None;
}
Optional<ConstraintFix *> getFix() const {
if (!IsRestriction)
return TheFix;
return None;
}
};
class ExpressionTimer {
Expr* E;
ASTContext &Context;
llvm::TimeRecord StartTime;
bool PrintDebugTiming;
bool PrintWarning;
public:
ExpressionTimer(Expr *E, ConstraintSystem &CS);
~ExpressionTimer();
unsigned getWarnLimit() const {
return Context.TypeCheckerOpts.WarnLongExpressionTypeChecking;
}
llvm::TimeRecord startedAt() const { return StartTime; }
/// Return the elapsed process time (including fractional seconds)
/// as a double.
double getElapsedProcessTimeInFractionalSeconds() const {
llvm::TimeRecord endTime = llvm::TimeRecord::getCurrentTime(false);
return endTime.getProcessTime() - StartTime.getProcessTime();
}
// Disable emission of warnings about expressions that take longer
// than the warning threshold.
void disableWarning() { PrintWarning = false; }
bool isExpired(unsigned thresholdInMillis) const {
auto elapsed = getElapsedProcessTimeInFractionalSeconds();
return unsigned(elapsed) > thresholdInMillis;
}
};
} // 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 type 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,
};
/// 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;
/// Index into the list of type variables, as used by the
/// constraint graph.
unsigned GraphIndex;
friend class constraints::SavedTypeVariableBinding;
public:
/// Retrieve the type variable associated with this implementation.
TypeVariableType *getTypeVariable() {
return reinterpret_cast<TypeVariableType *>(this) - 1;
}
/// Retrieve the type variable associated with this implementation.
const TypeVariableType *getTypeVariable() const {
return reinterpret_cast<const TypeVariableType *>(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
&& "Trucation");
}
/// Whether this type variable can bind to an lvalue type.
bool canBindToLValue() const { return getRawOptions() & TVO_CanBindToLValue; }
/// Whether this type variable can bind to an inout type.
bool canBindToInOut() const { return getRawOptions() & TVO_CanBindToInOut; }
/// Whether this type variable can bind to an inout type.
bool canBindToNoEscape() const { return getRawOptions() & TVO_CanBindToNoEscape; }
/// Whether this type variable can bind to a hole type.
bool canBindToHole() const { return getRawOptions() & TVO_CanBindToHole; }
/// 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;
}
/// Retrieve the index into the constraint graph's list of type variables.
unsigned getGraphIndex() const {
assert(GraphNode && "Graph node isn't set");
return GraphIndex;
}
/// Set the index into the constraint graph's list of type variables.
void setGraphIndex(unsigned newIndex) {
GraphIndex = newIndex;
}
/// Check whether this type variable either has a representative that
/// is not itself or has a fixed type binding.
bool hasRepresentativeOrFixed() const {
// If we have a fixed type, we're done.
if (!ParentOrFixed.is<TypeVariableType *>())
return true;
// Check whether the representative is different from our own type
// variable.
return ParentOrFixed.get<TypeVariableType *>() != getTypeVariable();
}
/// Record the current type-variable binding.
void recordBinding(constraints::SavedTypeVariableBindings &record) {
record.push_back(constraints::SavedTypeVariableBinding(getTypeVariable()));
}
/// 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.
Optional<ExprKind> getAtomicLiteralKind() const;
/// Determine whether this type variable represents a closure type.
bool isClosureType() 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;
/// Retrieve the representative of the equivalence class to which this
/// type variable belongs.
///
/// \param record 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::SavedTypeVariableBindings *record) {
// Find the representative type variable.
auto result = getTypeVariable();
Implementation *impl = this;
while (impl->ParentOrFixed.is<TypeVariableType *>()) {
// Extract the representative.
auto nextTV = impl->ParentOrFixed.get<TypeVariableType *>();
if (nextTV == result)
break;
result = nextTV;
impl = &nextTV->getImpl();
}
if (impl == this || !record)
return result;
// Perform path compression.
impl = this;
while (impl->ParentOrFixed.is<TypeVariableType *>()) {
// Extract the representative.
auto nextTV = impl->ParentOrFixed.get<TypeVariableType *>();
if (nextTV == result)
break;
// Record the state change.
impl->recordBinding(*record);
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 record The record of state changes.
void mergeEquivalenceClasses(TypeVariableType *other,
constraints::SavedTypeVariableBindings *record) {
// Merge the equivalence classes corresponding to these two type
// variables. Always merge 'up' the constraint stack, because it is simpler.
if (getID() > other->getImpl().getID()) {
other->getImpl().mergeEquivalenceClasses(getTypeVariable(), record);
return;
}
auto otherRep = other->getImpl().getRepresentative(record);
if (record)
otherRep->getImpl().recordBinding(*record);
otherRep->getImpl().ParentOrFixed = getTypeVariable();
if (canBindToLValue() && !otherRep->getImpl().canBindToLValue()) {
if (record)
recordBinding(*record);
getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToLValue;
}
if (canBindToInOut() && !otherRep->getImpl().canBindToInOut()) {
if (record)
recordBinding(*record);
getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToInOut;
}
if (canBindToNoEscape() && !otherRep->getImpl().canBindToNoEscape()) {
if (record)
recordBinding(*record);
getTypeVariable()->Bits.TypeVariableType.Options &= ~TVO_CanBindToNoEscape;
}
}
/// 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 record 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::SavedTypeVariableBindings *record) {
// Find the representative type variable.
auto rep = getRepresentative(record);
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::SavedTypeVariableBindings *record) {
assert((!getFixedType(0) || getFixedType(0)->isEqual(type)) &&
"Already has a fixed type!");
auto rep = getRepresentative(record);
if (record)
rep->getImpl().recordBinding(*record);
rep->getImpl().ParentOrFixed = type.getPointer();
}
void setCanBindToLValue(constraints::SavedTypeVariableBindings *record,
bool enabled) {
auto &impl = getRepresentative(record)->getImpl();
if (record)
impl.recordBinding(*record);
if (enabled)
impl.getTypeVariable()->Bits.TypeVariableType.Options |=
TVO_CanBindToLValue;
else
impl.getTypeVariable()->Bits.TypeVariableType.Options &=
~TVO_CanBindToLValue;
}
void setCanBindToNoEscape(constraints::SavedTypeVariableBindings *record,
bool enabled) {
auto &impl = getRepresentative(record)->getImpl();
if (record)
impl.recordBinding(*record);
if (enabled)
impl.getTypeVariable()->Bits.TypeVariableType.Options |=
TVO_CanBindToNoEscape;
else
impl.getTypeVariable()->Bits.TypeVariableType.Options &=
~TVO_CanBindToNoEscape;
}
void enableCanBindToHole(constraints::SavedTypeVariableBindings *record) {
auto &impl = getRepresentative(record)->getImpl();
if (record)
impl.recordBinding(*record);
impl.getTypeVariable()->Bits.TypeVariableType.Options |= TVO_CanBindToHole;
}
/// Print the type variable to the given output stream.
void print(llvm::raw_ostream &OS);
};
namespace constraints {
template <typename T = Expr> T *castToExpr(ASTNode node) {
return cast<T>(node.get<Expr *>());
}
template <typename T = Expr> T *getAsExpr(ASTNode node) {
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() || !node.is<Expr *>())
return false;
auto *E = node.get<Expr *>();
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;
}
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 produced by referring to this overload.
const Type openedType;
/// The type that this overload binds. Note that this may differ from
/// openedType, 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 {
Expr *ArgListExpr;
Expr *ArgExpr;
unsigned ArgIdx;
Type ArgType;
unsigned ParamIdx;
Type FnInterfaceType;
FunctionType *FnType;
const ValueDecl *Callee;
public:
FunctionArgApplyInfo(Expr *argListExpr, Expr *argExpr, unsigned argIdx,
Type argType, unsigned paramIdx, Type fnInterfaceType,
FunctionType *fnType, const ValueDecl *callee)
: ArgListExpr(argListExpr), ArgExpr(argExpr), ArgIdx(argIdx),
ArgType(argType), ParamIdx(paramIdx), FnInterfaceType(fnInterfaceType),
FnType(fnType), Callee(callee) {}
/// \returns The list of the arguments used for this application.
Expr *getArgListExpr() const { return ArgListExpr; }
/// \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 {
if (auto *te = dyn_cast<TupleExpr>(ArgListExpr))
return te->getElementName(ArgIdx);
assert(isa<ParenExpr>(ArgListExpr));
return Identifier();
}
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;
if (auto *te = dyn_cast<TupleExpr>(ArgListExpr))
return llvm::count(te->getElementNames(), argLabel) == 1;
return false;
};
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 {
if (auto trailingClosureArg =
ArgListExpr->getUnlabeledTrailingClosureIndexOfPackedArgument())
return ArgIdx >= *trailingClosureArg;
return false;
}
/// \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 = 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)->mapTypeOutOfContext();
}
return getParamTypeImpl(interfaceFnTy, lookThroughAutoclosure);
}
/// \returns The flags of the parameter which the argument is being applied
/// to.
ParameterTypeFlags getParameterFlags() const {
return FnType->getParams()[ParamIdx].getParameterFlags();
}
ParameterTypeFlags getParameterFlagsAtIndex(unsigned idx) const {
return FnType->getParams()[idx].getParameterFlags();
}
};
/// Describes an aspect of a solution that affects its overall score, i.e., a
/// user-defined conversions.
enum ScoreKind {
// 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 an async function in a synchronous context, or
/// vice versa.
SK_AsyncSyncMismatch,
/// 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,
/// A user-defined conversion.
SK_UserConversion,
/// A non-trivial function conversion.
SK_FunctionConversion,
/// A literal expression bound to a non-default literal type.
SK_NonDefaultLiteral,
/// An implicit upcast conversion between collection types.
SK_CollectionUpcastConversion,
/// A value-to-optional conversion.
SK_ValueToOptional,
/// A conversion to an empty existential type ('Any' or '{}').
SK_EmptyExistentialConversion,
/// A key path application subscript.
SK_KeyPathSubscript,
/// A conversion from a string, array, or inout to a pointer.
SK_ValueToPointerConversion,
SK_LastScoreKind = SK_ValueToPointerConversion,
};
/// 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.
Type bodyResultType;
/// An expression whose value has been recorded for later use.
struct RecordedExpr {
/// The temporary value that captures the value of the expression, if
/// there is one.
VarDecl *temporaryVar;
/// The expression that results from generating constraints with this
/// particular builder.
Expr *generatedExpr;
};
/// A mapping from expressions whose values are captured by the builder
/// to information about the temporary variable capturing the
llvm::DenseMap<Expr *, RecordedExpr> capturedExprs;
/// A mapping from statements to a pair containing the implicit variable
/// declaration that captures the result of that expression, and the
/// set of expressions that can be used to produce a value for that
/// variable.
llvm::DenseMap<Stmt *, std::pair<VarDecl *, llvm::TinyPtrVector<Expr *>>>
capturedStmts;
/// The return expression, capturing the last value to be emitted.
Expr *returnExpr = nullptr;
};
/// 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) {
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) {
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);
}
};
/// Display a score.
llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score);
/// Describes a dependent type that has been opened to a particular type
/// variable.
using OpenedType = std::pair<GenericTypeParamType *, TypeVariableType *>;
using OpenedTypeMap =
llvm::DenseMap<GenericTypeParamType *, TypeVariableType *>;
/// Describes contextual type information about a particular expression
/// within a constraint system.
struct ContextualTypeInfo {
TypeLoc typeLoc;
ContextualTypePurpose purpose;
ContextualTypeInfo() : typeLoc(TypeLoc()), purpose(CTP_Unused) {}
ContextualTypeInfo(Type contextualTy, ContextualTypePurpose purpose)
: typeLoc(TypeLoc::withoutLoc(contextualTy)), purpose(purpose) {}
ContextualTypeInfo(TypeLoc typeLoc, ContextualTypePurpose purpose)
: typeLoc(typeLoc), purpose(purpose) {}
Type getType() const { return typeLoc.getType(); }
};
/// Describes the information about a case label item that needs to be tracked
/// within the constraint system.
struct CaseLabelItemInfo {
Pattern *pattern;
Expr *guardExpr;
};
/// Describes information about a for-each loop that needs to be tracked
/// within the constraint system.
struct ForEachStmtInfo {
ForEachStmt *stmt;
/// The type of the sequence.
Type sequenceType;
/// The type of the iterator.
Type iteratorType;
/// The type of an element in the sequence.
Type elementType;
/// The type of the pattern that matches the elements.
Type initType;
/// The "where" expression, if there is one.
Expr *whereExpr;
};
/// Key to the constraint solver's mapping from AST nodes to their corresponding
/// solution application targets.
class SolutionApplicationTargetsKey {
public:
enum class Kind {
empty,
tombstone,
stmtCondElement,
stmt,
patternBindingEntry,
varDecl,
};
private:
Kind kind;
union {
const StmtConditionElement *stmtCondElement;
const Stmt *stmt;
struct PatternBindingEntry {
const PatternBindingDecl *patternBinding;
unsigned index;
} patternBindingEntry;
const VarDecl *varDecl;
} storage;
public:
SolutionApplicationTargetsKey(Kind kind) {
assert(kind == Kind::empty || kind == Kind::tombstone);
this->kind = kind;
}
SolutionApplicationTargetsKey(const StmtConditionElement *stmtCondElement) {
kind = Kind::stmtCondElement;
storage.stmtCondElement = stmtCondElement;
}
SolutionApplicationTargetsKey(const Stmt *stmt) {
kind = Kind::stmt;
storage.stmt = stmt;
}
SolutionApplicationTargetsKey(
const PatternBindingDecl *patternBinding, unsigned index) {
kind = Kind::stmt;
storage.patternBindingEntry.patternBinding = patternBinding;
storage.patternBindingEntry.index = index;
}
SolutionApplicationTargetsKey(const VarDecl *varDecl) {
kind = Kind::varDecl;
storage.varDecl = varDecl;
}
friend bool operator==(
SolutionApplicationTargetsKey lhs, SolutionApplicationTargetsKey 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::stmt:
return lhs.storage.stmt == rhs.storage.stmt;
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;
}
llvm_unreachable("invalid SolutionApplicationTargetsKey kind");
}
friend bool operator!=(
SolutionApplicationTargetsKey lhs, SolutionApplicationTargetsKey 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::stmt:
return hash_combine(
DenseMapInfo<unsigned>::getHashValue(static_cast<unsigned>(kind)),
DenseMapInfo<void *>::getHashValue(storage.stmt));
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));
}
llvm_unreachable("invalid statement kind");
}
};
/// 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;
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::DenseMap<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.
llvm::SmallVector<ConstraintFix *, 4> Fixes;
/// For locators associated with call expressions, the trailing closure
/// matching rule that was applied.
llvm::SmallMapVector<ConstraintLocator*, TrailingClosureMatching, 4>
trailingClosureMatchingChoices;
/// The set of disjunction choices used to arrive at this solution,
/// which informs constraint application.
llvm::DenseMap<ConstraintLocator *, unsigned> DisjunctionChoices;
/// 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 *, OpenedArchetypeType *>
OpenedExistentialTypes;
/// The locators of \c Defaultable constraints whose defaults were used.
llvm::SmallPtrSet<ConstraintLocator *, 2> DefaultedConstraints;
/// The node -> type mappings introduced by this solution.
llvm::DenseMap<ASTNode, Type> nodeTypes;
/// Contextual types introduced by this solution.
std::vector<std::pair<ASTNode, ContextualTypeInfo>> contextualTypes;
/// Maps AST nodes to their solution application targets.
llvm::MapVector<SolutionApplicationTargetsKey, SolutionApplicationTarget>
solutionApplicationTargets;
/// Maps case label items to information tracked about them as they are
/// being solved.
llvm::SmallMapVector<const CaseLabelItem *, CaseLabelItemInfo, 4>
caseLabelItems;
std::vector<std::pair<ConstraintLocator *, ProtocolConformanceRef>>
Conformances;
/// The set of functions that have been transformed by a result builder.
llvm::MapVector<AnyFunctionRef, AppliedBuilderTransform>
resultBuilderTransformed;
/// Simplify the given type by substituting all occurrences of
/// type variables for their fixed types.
Type simplifyType(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.
///
/// \param typeFromPattern Optionally, the caller can specify the pattern
/// from where the toType is derived, so that we can deliver better fixit.
///
/// \returns the coerced expression, which will have type \c ToType.
Expr *coerceToType(Expr *expr, Type toType,
ConstraintLocator *locator,
Optional<Pattern*> typeFromPattern = None);
/// Compute the set of substitutions for a generic signature opened at the
/// given locator.
///
/// \param sig The generic signature.
///
/// \param locator The locator that describes where the substitutions came
/// from.
SubstitutionMap computeSubstitutions(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;
}
/// Retrieve the fixed score of this solution
const Score &getFixedScore() const { return FixedScore; }
/// Retrieve the fixed score of this solution
Score &getFixedScore() { return FixedScore; }
/// Retrieve the fixed type for the given type variable.
Type getFixedType(TypeVariableType *typeVar) const;
/// Try to resolve the given locator to a declaration within this
/// solution. Note that this only returns a decl for a direct reference such
/// as \c x.foo and will not return a decl for \c x.foo().
ConcreteDeclRef resolveLocatorToDecl(ConstraintLocator *locator) const;
/// Retrieve the overload choice associated with the given
/// locator.
SelectedOverload getOverloadChoice(ConstraintLocator *locator) const {
return *getOverloadChoiceIfAvailable(locator);
}
/// Retrieve the overload choice associated with the given
/// locator.
Optional<SelectedOverload>
getOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
auto known = overloadChoices.find(locator);
if (known != overloadChoices.end())
return known->second;
return None;
}
/// Retrieve a fully-resolved protocol conformance at the given locator
/// and with the given protocol.
ProtocolConformanceRef resolveConformance(ConstraintLocator *locator,
ProtocolDecl *proto);
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;
/// Retrieve the type of the given node, as recorded in this solution.
Type getType(ASTNode node) 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;
/// Resolve type variables present in the raw type, using generic parameter
/// types where possible.
Type resolveInterfaceType(Type type) 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.
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;
}
/// 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;
SWIFT_DEBUG_DUMP;
/// Dump this solution.
void dump(raw_ostream &OS) const LLVM_ATTRIBUTE_USED;
};
/// Describes the differences between several solutions to the same
/// constraint system.
class SolutionDiff {
public:
/// A difference between two overloads.
struct OverloadDiff {
/// The locator that describes where the overload comes from.
ConstraintLocator *locator;
/// The choices that each solution made.
SmallVector<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, the client wants a best-effort solution to the constraint system,
/// but can tolerate a solution where all of the constraints are solved, but
/// not all type variables have been determined. In this case, the constraint
/// system is not applied to the expression AST, but the ConstraintSystem is
/// left in-tact.
AllowUnresolvedTypeVariables = 0x04,
/// If set, constraint system always reuses type of pre-typechecked
/// expression, and doesn't dig into its subexpressions.
ReusePrecheckedType = 0x08,
/// 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 be automatically enabled
/// for a pre-configured set of expressions on line numbers by setting
/// \c DebugConstraintSolverOnLines.
DebugConstraints = 0x10,
/// Don't try to type check closure bodies, and leave them unchecked. This is
/// used for source tooling functionalities.
LeaveClosureBodyUnchecked = 0x20,
/// If set, we are solving specifically to determine the type of a
/// CodeCompletionExpr, and should continue in the presence of errors wherever
/// possible.
ForCodeCompletion = 0x40,
};
/// 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 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);
}
Optional<unsigned> getFavoredIndex() const {
return (FavoredChoice == ~0U) ? 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);
}
/// 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();
}
};
/// Describes the target to which a constraint system's solution can be
/// applied.
class SolutionApplicationTarget {
public:
enum class Kind {
expression,
function,
stmtCondition,
caseLabelItem,
patternBinding,
uninitializedWrappedVar,
} kind;
private:
union {
struct {
/// The expression being type-checked.
Expr *expression;
/// The declaration context in which the expression is being
/// type-checked.
DeclContext *dc;
/// The purpose of the contextual type.
ContextualTypePurpose contextualPurpose;
/// The type to which the expression should be converted.
TypeLoc convertType;
/// When initializing a pattern from the expression, this is the
/// pattern.
Pattern *pattern;
struct {
/// The variable to which property wrappers have been applied, if
/// this is an initialization involving a property wrapper.
VarDecl *wrappedVar;
/// The innermost call to \c init(wrappedValue:), if this is an
/// initialization involving a property wrapper.
ApplyExpr *innermostWrappedValueInit;
/// Whether this property wrapper has an initial wrapped value specified
/// via \c = .
bool hasInitialWrappedValue;
} propertyWrapper;
/// Whether the expression result will be discarded at the end.
bool isDiscarded;
/// Whether to bind the variables encountered within the pattern to
/// fresh type variables via one-way constraints.
bool bindPatternVarsOneWay;
union {
struct {
/// The pattern binding declaration for an initialization, if any.
PatternBindingDecl *patternBinding;
/// The index into the pattern binding declaration, if any.
unsigned patternBindingIndex;
} initialization;
ForEachStmtInfo forEachStmt;
};
} expression;
struct {
AnyFunctionRef function;
BraceStmt *body;
} function;
struct {
StmtCondition stmtCondition;
DeclContext *dc;
} stmtCondition;
struct {
CaseLabelItem *caseLabelItem;
DeclContext *dc;
} caseLabelItem;
PatternBindingDecl *patternBinding;
VarDecl *uninitializedWrappedVar;
};
// If the pattern contains a single variable that has an attached
// property wrapper, set up the initializer expression to initialize
// the backing storage.
void maybeApplyPropertyWrapper();
public:
SolutionApplicationTarget(Expr *expr, DeclContext *dc,
ContextualTypePurpose contextualPurpose,
Type convertType, bool isDiscarded)
: SolutionApplicationTarget(expr, dc, contextualPurpose,
TypeLoc::withoutLoc(convertType),
isDiscarded) { }
SolutionApplicationTarget(Expr *expr, DeclContext *dc,
ContextualTypePurpose contextualPurpose,
TypeLoc convertType, bool isDiscarded);
SolutionApplicationTarget(AnyFunctionRef fn)
: SolutionApplicationTarget(fn, fn.getBody()) { }
SolutionApplicationTarget(StmtCondition stmtCondition, DeclContext *dc) {
kind = Kind::stmtCondition;
this->stmtCondition.stmtCondition = stmtCondition;
this->stmtCondition.dc = dc;
}
SolutionApplicationTarget(AnyFunctionRef fn, BraceStmt *body) {
kind = Kind::function;
function.function = fn;
function.body = body;
}
SolutionApplicationTarget(CaseLabelItem *caseLabelItem, DeclContext *dc) {
kind = Kind::caseLabelItem;
this->caseLabelItem.caseLabelItem = caseLabelItem;
this->caseLabelItem.dc = dc;
}
SolutionApplicationTarget(PatternBindingDecl *patternBinding) {
kind = Kind::patternBinding;
this->patternBinding = patternBinding;
}
SolutionApplicationTarget(VarDecl *wrappedVar) {
kind = Kind::uninitializedWrappedVar;
this->uninitializedWrappedVar= wrappedVar;
}
/// Form a target for the initialization of a pattern from an expression.
static SolutionApplicationTarget forInitialization(
Expr *initializer, DeclContext *dc, Type patternType, Pattern *pattern,
bool bindPatternVarsOneWay);
/// Form a target for the initialization of a pattern binding entry from
/// an expression.
static SolutionApplicationTarget forInitialization(
Expr *initializer, DeclContext *dc, Type patternType,
PatternBindingDecl *patternBinding, unsigned patternBindingIndex,
bool bindPatternVarsOneWay);
/// Form a target for a for-each loop.
static SolutionApplicationTarget forForEachStmt(
ForEachStmt *stmt, ProtocolDecl *sequenceProto, DeclContext *dc,
bool bindPatternVarsOneWay);
/// Form a target for a property with an attached property wrapper that is
/// initialized out-of-line.
static SolutionApplicationTarget forUninitializedWrappedVar(
VarDecl *wrappedVar);
Expr *getAsExpr() const {
switch (kind) {
case Kind::expression:
return expression.expression;
case Kind::function:
case Kind::stmtCondition:
case Kind::caseLabelItem:
case Kind::patternBinding:
case Kind::uninitializedWrappedVar:
return nullptr;
}
llvm_unreachable("invalid expression type");
}
DeclContext *getDeclContext() const {
switch (kind) {
case Kind::expression:
return expression.dc;
case Kind::function:
return function.function.getAsDeclContext();
case Kind::stmtCondition:
return stmtCondition.dc;
case Kind::caseLabelItem:
return caseLabelItem.dc;
case Kind::patternBinding:
return patternBinding->getDeclContext();
case Kind::uninitializedWrappedVar:
return uninitializedWrappedVar->getDeclContext();
}
llvm_unreachable("invalid decl context type");
}
ContextualTypePurpose getExprContextualTypePurpose() const {
assert(kind == Kind::expression);
return expression.contextualPurpose;
}
Type getExprContextualType() const {
return getExprContextualTypeLoc().getType();
}
TypeLoc getExprContextualTypeLoc() const {
assert(kind == Kind::expression);
// For an @autoclosure parameter, the conversion type is
// the result of the function type.
if (FunctionType *autoclosureParamType = getAsAutoclosureParamType()) {
return TypeLoc(expression.convertType.getTypeRepr(),
autoclosureParamType->getResult());
}
return expression.convertType;
}
/// Retrieve the type to which an expression should be converted, or
/// a NULL type if no conversion constraint should be generated.
Type getExprConversionType() const {
if (contextualTypeIsOnlyAHint())
return Type();
return getExprContextualType();
}
/// Returns the autoclosure parameter type, or \c nullptr if the
/// expression has a different kind of context.
FunctionType *getAsAutoclosureParamType() const {
assert(kind == Kind::expression);
if (expression.contextualPurpose == CTP_AutoclosureDefaultParameter)
return expression.convertType.getType()->castTo<FunctionType>();
return nullptr;
}
void setExprConversionType(Type type) {
assert(kind == Kind::expression);
expression.convertType = TypeLoc::withoutLoc(type);
}
void setExprConversionTypeLoc(TypeLoc type) {
assert(kind == Kind::expression);
expression.convertType = type;
}
/// For a pattern initialization target, retrieve the pattern.
Pattern *getInitializationPattern() const {
assert(kind == Kind::expression);
assert(expression.contextualPurpose == CTP_Initialization);
return expression.pattern;
}
/// For a pattern initialization target, retrieve the contextual pattern.
ContextualPattern getContextualPattern() const;
/// Whether this target is for a for-in statement.
bool isForEachStmt() const {
return kind == Kind::expression &&
getExprContextualTypePurpose() == CTP_ForEachStmt;
}
/// Whether this is an initialization for an Optional.Some pattern.
bool isOptionalSomePatternInit() const {
return kind == Kind::expression &&
expression.contextualPurpose == CTP_Initialization &&
isa<OptionalSomePattern>(expression.pattern) &&
!expression.pattern->isImplicit();
}
/// Whether to bind the types of any variables within the pattern via
/// one-way constraints.
bool shouldBindPatternVarsOneWay() const {
return kind == Kind::expression && expression.bindPatternVarsOneWay;
}
/// Whether or not an opaque value placeholder should be injected into the
/// first \c wrappedValue argument of an apply expression.
bool shouldInjectWrappedValuePlaceholder(ApplyExpr *apply) const {
if (kind != Kind::expression ||
expression.contextualPurpose != CTP_Initialization)
return false;
auto *wrappedVar = expression.propertyWrapper.wrappedVar;
if (!apply || !wrappedVar || wrappedVar->isStatic())
return false;
return expression.propertyWrapper.innermostWrappedValueInit == apply;
}
/// Whether this target is for initialization of a property wrapper
/// with an initial wrapped value specified via \c = .
bool propertyWrapperHasInitialWrappedValue() const {
return (kind == Kind::expression &&
expression.propertyWrapper.hasInitialWrappedValue);
}
/// Retrieve the wrapped variable when initializing a pattern with a
/// property wrapper.
VarDecl *getInitializationWrappedVar() const {
assert(kind == Kind::expression);
assert(expression.contextualPurpose == CTP_Initialization);
return expression.propertyWrapper.wrappedVar;
}
PatternBindingDecl *getInitializationPatternBindingDecl() const {
assert(kind == Kind::expression);
assert(expression.contextualPurpose == CTP_Initialization);
return expression.initialization.patternBinding;
}
unsigned getInitializationPatternBindingIndex() const {
assert(kind == Kind::expression);
assert(expression.contextualPurpose == CTP_Initialization);
return expression.initialization.patternBindingIndex;
}
const ForEachStmtInfo &getForEachStmtInfo() const {
assert(isForEachStmt());
return expression.forEachStmt;
}
ForEachStmtInfo &getForEachStmtInfo() {
assert(isForEachStmt());
return expression.forEachStmt;
}
/// Whether this context infers an opaque return type.
bool infersOpaqueReturnType() const;
/// Whether the contextual type is only a hint, rather than a type
bool contextualTypeIsOnlyAHint() const;
bool isDiscardedExpr() const {
assert(kind == Kind::expression);
return expression.isDiscarded;
}
void setExpr(Expr *expr) {
assert(kind == Kind::expression);
expression.expression = expr;
}
void setPattern(Pattern *pattern) {
assert(kind == Kind::expression);
assert(expression.contextualPurpose == CTP_Initialization ||
expression.contextualPurpose == CTP_ForEachStmt);
expression.pattern = pattern;
}
Optional<AnyFunctionRef> getAsFunction() const {
switch (kind) {
case Kind::expression:
case Kind::stmtCondition:
case Kind::caseLabelItem:
case Kind::patternBinding:
case Kind::uninitializedWrappedVar:
return None;
case Kind::function:
return function.function;
}
llvm_unreachable("invalid function kind");
}
Optional<StmtCondition> getAsStmtCondition() const {
switch (kind) {
case Kind::expression:
case Kind::function:
case Kind::caseLabelItem:
case Kind::patternBinding:
case Kind::uninitializedWrappedVar:
return None;
case Kind::stmtCondition:
return stmtCondition.stmtCondition;
}
llvm_unreachable("invalid statement kind");
}
Optional<CaseLabelItem *> getAsCaseLabelItem() const {
switch (kind) {
case Kind::expression:
case Kind::function:
case Kind::stmtCondition:
case Kind::patternBinding:
case Kind::uninitializedWrappedVar:
return None;
case Kind::caseLabelItem:
return caseLabelItem.caseLabelItem;
}
llvm_unreachable("invalid case label type");
}
PatternBindingDecl *getAsPatternBinding() const {
switch (kind) {
case Kind::expression:
case Kind::function:
case Kind::stmtCondition:
case Kind::caseLabelItem:
case Kind::uninitializedWrappedVar:
return nullptr;
case Kind::patternBinding:
return patternBinding;
}
llvm_unreachable("invalid case label type");
}
VarDecl *getAsUninitializedWrappedVar() const {
switch (kind) {
case Kind::expression:
case Kind::function:
case Kind::stmtCondition:
case Kind::caseLabelItem:
case Kind::patternBinding:
return nullptr;
case Kind::uninitializedWrappedVar:
return uninitializedWrappedVar;
}
llvm_unreachable("invalid case label type");
}
BraceStmt *getFunctionBody() const {
assert(kind == Kind::function);
return function.body;
}
void setFunctionBody(BraceStmt *stmt) {
assert(kind == Kind::function);
function.body = stmt;
}
/// Retrieve the source range of the target.
SourceRange getSourceRange() const {
switch (kind) {
case Kind::expression:
return expression.expression->getSourceRange();
case Kind::function:
return function.body->getSourceRange();
case Kind::stmtCondition:
return SourceRange(stmtCondition.stmtCondition.front().getStartLoc(),
stmtCondition.stmtCondition.back().getEndLoc());
case Kind::caseLabelItem:
return caseLabelItem.caseLabelItem->getSourceRange();
case Kind::patternBinding:
return patternBinding->getSourceRange();
case Kind::uninitializedWrappedVar:
return uninitializedWrappedVar->getSourceRange();
}
llvm_unreachable("invalid target type");
}
/// Retrieve the source location for the target.
SourceLoc getLoc() const {
switch (kind) {
case Kind::expression:
return expression.expression->getLoc();
case Kind::function:
return function.function.getLoc();
case Kind::stmtCondition:
return stmtCondition.stmtCondition.front().getStartLoc();
case Kind::caseLabelItem:
return caseLabelItem.caseLabelItem->getStartLoc();
case Kind::patternBinding:
return patternBinding->getLoc();
case Kind::uninitializedWrappedVar:
return uninitializedWrappedVar->getLoc();
}
llvm_unreachable("invalid target type");
}
/// Walk the contents of the application target.
SolutionApplicationTarget walk(ASTWalker &walker);
};
/// A function that rewrites a solution application target in the context
/// of solution application.
using RewriteTargetFn = std::function<
Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>;
enum class ConstraintSystemPhase {
ConstraintGeneration,
Solving,
Diagnostics,
Finalization
};
/// Describes the result of applying a solution to a given function.
enum class SolutionApplicationToFunctionResult {
/// Application of the solution succeeded.
Success,
/// Application of the solution failed.
/// TODO: This should probably go away entirely.
Failure,
/// The solution could not be applied immediately, and type checking for
/// this function should be delayed until later.
Delay,
};
/// Describes a system of constraints on type variables, the
/// solution of which assigns concrete types to each of the type variables.
/// Constraint systems are typically generated given an (untyped) expression.
class ConstraintSystem {
ASTContext &Context;
friend class swift::unittest::SemaTest;
public:
DeclContext *DC;
ConstraintSystemOptions Options;
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 RequirementFailure;
friend class MissingMemberFailure;
class SolverScope;
/// 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:
/// A constraint that has failed along the current solver path.
/// Do not set directly, call \c recordFailedConstraint instead.
Constraint *failedConstraint = nullptr;
/// Current phase of the constraint system lifetime.
ConstraintSystemPhase Phase = ConstraintSystemPhase::ConstraintGeneration;
/// The set of expressions for which we have generated constraints.
llvm::SetVector<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 scopes created so far during the solution
/// of this constraint system.
///
/// This is a measure of complexity of the solution space. A new
/// scope is created every time we attempt a type variable binding
/// or explore an option in a disjunction.
unsigned CountScopes = 0;
/// High-water mark of measured memory usage in any sub-scope we
/// explored.
size_t MaxMemory = 0;
/// Cached member lookups.
llvm::DenseMap<std::pair<Type, DeclNameRef>, Optional<LookupResult>>
MemberLookups;
/// Cached sets of "alternative" literal types.
static const unsigned NumAlternativeLiteralTypes = 13;
Optional<ArrayRef<Type>> AlternativeLiteralTypes[NumAlternativeLiteralTypes];
/// 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::MapVector<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.
llvm::MapVector<const ClosureExpr *, FunctionType *> ClosureTypes;
/// 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;
/// 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::MapVector<ASTNode, Type> NodeTypes;
llvm::DenseMap<std::pair<const KeyPathExpr *, unsigned>, TypeBase *>
KeyPathComponentTypes;
/// Maps AST entries to their solution application targets.
llvm::MapVector<SolutionApplicationTargetsKey, SolutionApplicationTarget>
solutionApplicationTargets;
/// Contextual type information for expressions that are part of this
/// constraint system.
llvm::MapVector<ASTNode, ContextualTypeInfo> contextualTypes;
/// Information about each case label item tracked by the constraint system.
llvm::SmallMapVector<const CaseLabelItem *, CaseLabelItemInfo, 4>
caseLabelItems;
/// 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.
std::vector<std::tuple<Type, Type, ConversionRestrictionKind>>
ConstraintRestrictions;
/// The set of fixes applied to make the solution work.
llvm::SmallVector<ConstraintFix *, 4> Fixes;
/// The set of remembered disjunction choices used to reach
/// the current constraint system.
std::vector<std::pair<ConstraintLocator*, unsigned>>
DisjunctionChoices;
/// For locators associated with call expressions, the trailing closure
/// matching rule that was applied.
std::vector<std::pair<ConstraintLocator*, TrailingClosureMatching>>
trailingClosureMatchingChoices;
/// 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.
SmallVector<std::pair<ConstraintLocator *, ArrayRef<OpenedType>>, 4>
OpenedTypes;
/// The list of all generic requirements fixed along the current
/// solver path.
using FixedRequirement =
std::tuple<GenericTypeParamType *, unsigned, TypeBase *>;
llvm::SmallSetVector<FixedRequirement, 4> FixedRequirements;
bool isFixedRequirement(ConstraintLocator *reqLocator, Type requirementTy);
void recordFixedRequirement(ConstraintLocator *reqLocator,
Type requirementTy);
/// A mapping from constraint locators to the opened existential archetype
/// used for the 'self' of an existential type.
SmallVector<std::pair<ConstraintLocator *, OpenedArchetypeType *>, 4>
OpenedExistentialTypes;
/// The nodes for which we have produced types, along with the prior type
/// each node had before introducing this type.
llvm::SmallVector<std::pair<ASTNode, Type>, 8> addedNodeTypes;
std::vector<std::pair<ConstraintLocator *, ProtocolConformanceRef>>
CheckedConformances;
/// The set of functions that have been transformed by a result builder.
std::vector<std::pair<AnyFunctionRef, AppliedBuilderTransform>>
resultBuilderTransformed;
/// Cache of the effects any closures visited.
llvm::SmallDenseMap<ClosureExpr *, FunctionType::ExtInfo, 4> closureEffectsCache;
public:
/// The locators of \c Defaultable constraints whose defaults were used.
std::vector<ConstraintLocator *> DefaultedConstraints;
/// A cache that stores the @dynamicCallable required methods implemented by
/// types.
llvm::DenseMap<CanType, DynamicCallableMethods> DynamicCallableCache;
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::SmallDenseSet<OverloadSetRefExpr *> &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::SmallDenseSet<OverloadSetRefExpr *> &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::SmallDenseSet<OverloadSetRefExpr *> &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;
}
}
unsigned threshold =
cs->getASTContext().TypeCheckerOpts.SolverShrinkUnsolvedThreshold;
return unsolvedDisjunctions >= threshold;
}
};
/// Describes the current solver state.
struct SolverState {
SolverState(ConstraintSystem &cs,
FreeTypeVariableBinding allowFreeTypeVariables);
~SolverState();
/// The constraint system.
ConstraintSystem &CS;
FreeTypeVariableBinding AllowFreeTypeVariables;
/// Depth of the solution stack.
unsigned depth = 0;
/// Maximum depth reached so far in exploring solutions.
unsigned maxDepth = 0;
/// Whether to record failures or not.
bool recordFixes = false;
/// The set of type variable bindings that have changed while
/// processing this constraint system.
SavedTypeVariableBindings savedBindings;
/// The best solution computed so far.
Optional<Score> BestScore;
/// The number of the solution attempt we're looking at.
unsigned SolutionAttempt;
/// Refers to the innermost partial solution scope.
SolverScope *PartialSolutionScope = nullptr;
// Statistics
#define CS_STATISTIC(Name, Description) unsigned Name = 0;
#include "ConstraintSolverStats.def"
/// Check whether there are any retired constraints present.
bool hasRetiredConstraints() const {
return !retiredConstraints.empty();
}
/// Mark given constraint as retired along current solver path.
///
/// \param constraint The constraint to retire temporarily.
void retireConstraint(Constraint *constraint) {
retiredConstraints.push_front(constraint);
}
/// Iterate over all of the retired constraints registered with
/// current solver state.
///
/// \param processor The processor function to be applied to each of
/// the constraints retrieved.
void forEachRetired(llvm::function_ref<void(Constraint &)> processor) {
for (auto &constraint : retiredConstraints)
processor(constraint);
}
/// Add new "generated" constraint along the current solver path.
///
/// \param constraint The newly generated constraint.
void addGeneratedConstraint(Constraint *constraint) {
assert(constraint && "Null generated constraint?");
generatedConstraints.push_back(constraint);
}
/// Register given scope to be tracked by the current solver state,
/// this helps to make sure that all of the retired/generated constraints
/// are dealt with correctly when the life time of the scope ends.
///
/// \param scope The scope to associate with current solver state.
void registerScope(SolverScope *scope) {
++depth;
maxDepth = std::max(maxDepth, depth);
scope->scopeNumber = NumStatesExplored++;
CS.incrementScopeCounter();
auto scopeInfo =
std::make_tuple(scope, retiredConstraints.begin(),
generatedConstraints.size());
scopes.push_back(scopeInfo);
}
/// Restore all of the retired/generated constraints to the state
/// before given scope. This is required because retired constraints have
/// to be re-introduced to the system in order of arrival (LIFO) and list
/// of the generated constraints has to be truncated back to the
/// original size.
///
/// \param scope The solver scope to rollback.
void rollback(SolverScope *scope) {
--depth;
unsigned countScopesExplored = NumStatesExplored - scope->scopeNumber;
if (countScopesExplored == 1)
CS.incrementLeafScopes();
SolverScope *savedScope;
// The position of last retired constraint before given scope.
ConstraintList::iterator lastRetiredPos;
// The original number of generated constraints before given scope.
unsigned numGenerated;
std::tie(savedScope, lastRetiredPos, numGenerated) =
scopes.pop_back_val();
assert(savedScope == scope && "Scope rollback not in LIFO order!");
// Restore all of the retired constraints.
CS.InactiveConstraints.splice(CS.InactiveConstraints.end(),
retiredConstraints,
retiredConstraints.begin(), lastRetiredPos);
// And remove all of the generated constraints.
auto genStart = generatedConstraints.begin() + numGenerated,
genEnd = generatedConstraints.end();
for (auto genI = genStart; genI != genEnd; ++genI) {
CS.InactiveConstraints.erase(ConstraintList::iterator(*genI));
}
generatedConstraints.erase(genStart, genEnd);
for (unsigned constraintIdx :
range(scope->numDisabledConstraints, disabledConstraints.size())) {
if (disabledConstraints[constraintIdx]->isDisabled())
disabledConstraints[constraintIdx]->setEnabled();
}
disabledConstraints.erase(
disabledConstraints.begin() + scope->numDisabledConstraints,
disabledConstraints.end());
for (unsigned constraintIdx :
range(scope->numFavoredConstraints, favoredConstraints.size())) {
if (favoredConstraints[constraintIdx]->isFavored())
favoredConstraints[constraintIdx]->setFavored(false);
}
favoredConstraints.erase(
favoredConstraints.begin() + scope->numFavoredConstraints,
favoredConstraints.end());
}
/// Check whether constraint system is allowed to form solutions
/// even with unbound type variables present.
bool allowsFreeTypeVariables() const {
return AllowFreeTypeVariables != FreeTypeVariableBinding::Disallow;
}
unsigned getNumDisabledConstraints() const {
return disabledConstraints.size();
}
/// Disable the given constraint; this change will be rolled back
/// when we exit the current solver scope.
void disableContraint(Constraint *constraint) {
constraint->setDisabled();
disabledConstraints.push_back(constraint);
}
unsigned getNumFavoredConstraints() const {
return favoredConstraints.size();
}
/// 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();
favoredConstraints.push_back(constraint);
}
private:
/// The list of constraints that have been retired along the
/// current path, this list is used in LIFO fashion when constraints
/// are added back to the circulation.
ConstraintList retiredConstraints;
/// 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;
/// The current set of generated constraints.
SmallVector<Constraint *, 4> generatedConstraints;
/// The collection which holds association between solver scope
/// and position of the last retired constraint and number of
/// constraints generated before registration of given scope,
/// this helps to rollback all of the constraints retired/generated
/// each of the registered scopes correct (LIFO) order.
llvm::SmallVector<
std::tuple<SolverScope *, ConstraintList::iterator, unsigned>, 4> scopes;
SmallVector<Constraint *, 4> disabledConstraints;
SmallVector<Constraint *, 4> favoredConstraints;
};
class CacheExprTypes : public ASTWalker {
Expr *RootExpr;
ConstraintSystem &CS;
bool ExcludeRoot;
public:
CacheExprTypes(Expr *expr, ConstraintSystem &cs, bool excludeRoot)
: RootExpr(expr), CS(cs), ExcludeRoot(excludeRoot) {}
Expr *walkToExprPost(Expr *expr) override {
if (ExcludeRoot && expr == RootExpr) {
assert(!expr->getType() && "Unexpected type in root of expression!");
return 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 expr;
}
/// Ignore statements.
std::pair<bool, Stmt *> walkToStmtPre(Stmt *stmt) override {
return { false, stmt };
}
/// Ignore declarations.
bool walkToDeclPre(Decl *decl) override { return false; }
};
public:
/// Retrieve the first constraint that has failed along the solver's path, or
/// \c nullptr if no constraint has failed.
Constraint *getFailedConstraint() const { return failedConstraint; }
ConstraintSystemPhase getPhase() const { return Phase; }
/// Move constraint system to a new phase of its lifetime.
void setPhase(ConstraintSystemPhase newPhase) {
if (Phase == newPhase)
return;
#ifndef NDEBUG
switch (Phase) {
case ConstraintSystemPhase::ConstraintGeneration:
assert(newPhase == ConstraintSystemPhase::Solving);
break;
case ConstraintSystemPhase::Solving:
// We can come back to constraint generation phase while
// processing result builder body.
assert(newPhase == ConstraintSystemPhase::ConstraintGeneration ||
newPhase == ConstraintSystemPhase::Diagnostics ||
newPhase == ConstraintSystemPhase::Finalization);
break;
case ConstraintSystemPhase::Diagnostics:
assert(newPhase == ConstraintSystemPhase::Solving ||
newPhase == ConstraintSystemPhase::Finalization);
break;
case ConstraintSystemPhase::Finalization:
assert(newPhase == ConstraintSystemPhase::Diagnostics);
break;
}
#endif
Phase = newPhase;
}
/// 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;
struct ArgumentInfo {
ArrayRef<Identifier> Labels;
Optional<unsigned> UnlabeledTrailingClosureIndex;
};
/// A mapping from the constraint locators for references to various
/// names (e.g., member references, normal name references, possible
/// constructions) to the argument labels provided in the call to
/// that locator.
llvm::DenseMap<ConstraintLocator *, ArgumentInfo> ArgumentInfos;
/// 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 info that is associated with a member
/// reference at the given locator.
Optional<ArgumentInfo> getArgumentInfo(ConstraintLocator *locator);
Optional<SelectedOverload>
findSelectedOverloadFor(ConstraintLocator *locator) const {
auto result = ResolvedOverloads.find(locator);
if (result == ResolvedOverloads.end())
return None;
return result->second;
}
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.
///
///
class SolverScope {
ConstraintSystem &cs;
/// The length of \c TypeVariables.
unsigned numTypeVariables;
/// The length of \c SavedBindings.
unsigned numSavedBindings;
/// The length of \c ConstraintRestrictions.
unsigned numConstraintRestrictions;
/// The length of \c Fixes.
unsigned numFixes;
/// The length of \c FixedRequirements.
unsigned numFixedRequirements;
/// The length of \c DisjunctionChoices.
unsigned numDisjunctionChoices;
/// The length of \c trailingClosureMatchingChoices;
unsigned numTrailingClosureMatchingChoices;
/// The length of \c OpenedTypes.
unsigned numOpenedTypes;
/// The length of \c OpenedExistentialTypes.
unsigned numOpenedExistentialTypes;
/// The length of \c DefaultedConstraints.
unsigned numDefaultedConstraints;
unsigned numAddedNodeTypes;
unsigned numCheckedConformances;
unsigned numDisabledConstraints;
unsigned numFavoredConstraints;
unsigned numResultBuilderTransformed;
/// The length of \c ResolvedOverloads.
unsigned numResolvedOverloads;
/// The length of \c ClosureTypes.
unsigned numInferredClosureTypes;
/// The length of \c contextualTypes.
unsigned numContextualTypes;
/// The length of \c solutionApplicationTargets.
unsigned numSolutionApplicationTargets;
/// The length of \c caseLabelItems.
unsigned numCaseLabelItems;
/// The previous score.
Score PreviousScore;
/// The scope number of this scope. Set when the scope is registered.
unsigned scopeNumber = 0;
/// Constraint graph scope associated with this solver scope.
ConstraintGraphScope CGScope;
SolverScope(const SolverScope &) = delete;
SolverScope &operator=(const SolverScope &) = delete;
friend class ConstraintSystem;
public:
explicit SolverScope(ConstraintSystem &cs);
~SolverScope();
};
ConstraintSystem(DeclContext *dc,
ConstraintSystemOptions options);
~ConstraintSystem();
/// Retrieve the constraint graph associated with this constraint system.
ConstraintGraph &getConstraintGraph() const { 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 applySolution(const Solution &solution);
// FIXME: Perhaps these belong on ConstraintSystem itself.
friend Optional<BraceStmt *>
swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func,
Type builderType);
friend Optional<SolutionApplicationTarget>
swift::TypeChecker::typeCheckExpression(
SolutionApplicationTarget &target, OptionSet<TypeCheckExprFlags> options);
/// 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 idicates 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());
}
}
/// Restore the type variable bindings to what they were before
/// we attempted to solve this constraint system.
///
/// \param numBindings The number of bindings to restore, from the end of
/// the saved-binding stack.
void restoreTypeVariableBindings(unsigned numBindings);
/// Retrieve the set of saved type variable bindings, if available.
///
/// \returns null when we aren't currently solving the system.
SavedTypeVariableBindings *getSavedBindings() const {
return solverState ? &solverState->savedBindings : nullptr;
}
/// Add a new type variable that was already created.
void addTypeVariable(TypeVariableType *typeVar);
/// 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:
/// Lookup for a member with the given name 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);
/// Retrieve the set of "alternative" literal types that we'll explore
/// for a given literal protocol kind.
ArrayRef<Type> getAlternativeLiteralTypes(KnownProtocolKind kind);
/// Create a new type variable.
TypeVariableType *createTypeVariable(ConstraintLocator *locator,
unsigned options);
/// 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;
}
/// Whether the given expression's source range contains the code
/// completion location.
bool containsCodeCompletionLoc(Expr *expr) const;
void setClosureType(const ClosureExpr *closure, FunctionType *type) {
assert(closure);
assert(type && "Expected non-null type");
assert(ClosureTypes.count(closure) == 0 && "Cannot reset closure type");
ClosureTypes.insert({closure, type});
}
FunctionType *getClosureType(const ClosureExpr *closure) const {
auto result = ClosureTypes.find(closure);
assert(result != ClosureTypes.end());
return result->second;
}
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.
///
/// The side tables are used through the expression type checker to avoid mutating nodes until
/// we know we have successfully type-checked them.
void setType(ASTNode node, Type type) {
assert(!node.isNull() && "Cannot set type information on null node");
assert(type && "Expected non-null type");
// Record the type.
Type &entry = NodeTypes[node];
Type oldType = entry;
entry = type;
// Record the fact that we ascribed a type to this node.
addedNodeTypes.push_back({node, oldType});
}
/// 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(KeyPathExpr *KP, unsigned I, Type T) {
assert(KP && "Expected non-null key path parameter!");
assert(T && "Expected non-null type!");
KeyPathComponentTypes[std::make_pair(KP, I)] = T.getPointer();
}
/// 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;
}
bool hasType(const KeyPathExpr *KP, unsigned I) const {
assert(KP && "Expected non-null key path parameter!");
return KeyPathComponentTypes.find(std::make_pair(KP, I))
!= KeyPathComponentTypes.end();
}
/// Get the type for an node.
Type getType(ASTNode node) const {
assert(hasType(node) && "Expected type to have been set!");
// FIXME: lvalue differences
// assert((!E->getType() ||
// E->getType()->isEqual(ExprTypes.find(E)->second)) &&
// "Mismatched types!");
return NodeTypes.find(node)->second;
}
Type getType(const KeyPathExpr *KP, unsigned I) const {
assert(hasType(KP, I) && "Expected type to have been set!");
return KeyPathComponentTypes.find(std::make_pair(KP, I))->second;
}
/// Retrieve the type of the node, if known.
Type getTypeIfAvailable(ASTNode node) const {
auto known = NodeTypes.find(node);
if (known == NodeTypes.end())
return Type();
return known->second;
}
/// Retrieve type type of the given declaration to be used in
/// constraint system, this is better than calling `getType()`
/// directly because it accounts of constraint system flags.
Type getVarType(const VarDecl *var);
/// Cache the type of the expression argument and return that same
/// argument.
template <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 setContextualType(ASTNode node, TypeLoc T,
ContextualTypePurpose purpose) {
assert(bool(node) && "Expected non-null expression!");
assert(contextualTypes.count(node) == 0 &&
"Already set this contextual type");
contextualTypes[node] = {T, purpose};
}
Optional<ContextualTypeInfo> getContextualTypeInfo(ASTNode node) const {
auto known = contextualTypes.find(node);
if (known == contextualTypes.end())
return None;
return known->second;
}
Type getContextualType(ASTNode node) const {
auto result = getContextualTypeInfo(node);
if (result)
return result->typeLoc.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 setSolutionApplicationTarget(
SolutionApplicationTargetsKey key, SolutionApplicationTarget target) {
assert(solutionApplicationTargets.count(key) == 0 &&
"Already set this solution application target");
solutionApplicationTargets.insert({key, target});
}
Optional<SolutionApplicationTarget> getSolutionApplicationTarget(
SolutionApplicationTargetsKey key) const {
auto known = solutionApplicationTargets.find(key);
if (known == solutionApplicationTargets.end())
return None;
return known->second;
}
void setCaseLabelItemInfo(const CaseLabelItem *item, CaseLabelItemInfo info) {
assert(item != nullptr);
assert(caseLabelItems.count(item) == 0);
caseLabelItems[item] = info;
}
Optional<CaseLabelItemInfo> getCaseLabelItemInfo(
const CaseLabelItem *item) const {
auto known = caseLabelItems.find(item);
if (known == caseLabelItems.end())
return None;
return known->second;
}
/// Retrieve the constraint locator for the given anchor and
/// path, uniqued.
ConstraintLocator *
getConstraintLocator(ASTNode anchor,
ArrayRef<ConstraintLocator::PathElement> path,
unsigned summaryFlags);
/// Retrive 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::makeArrayRef(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;
}
/// Retrieve the depth of the given expression.
Optional<unsigned> getExprDepth(Expr *expr) {
if (auto result = getExprDepthAndParent(expr))
return result->first;
return None;
}
/// Retrieve the depth and parent expression of the given expression.
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,