mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The "skip generic operators" heuristic within the constraint solver is fairly awful, because it is known to miss reasonable solutions. Make is slightly more narrow by only skipping generic operators when the concrete operator we found was symmetric.
2753 lines
98 KiB
C++
2753 lines
98 KiB
C++
//===--- CSSolver.cpp - Constraint Solver ---------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the constraint solver used in the type checker.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "ConstraintSystem.h"
|
|
#include "ConstraintGraph.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/TypeWalker.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include <memory>
|
|
#include <tuple>
|
|
|
|
using namespace swift;
|
|
using namespace constraints;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Constraint solver statistics
|
|
//===----------------------------------------------------------------------===//
|
|
#define DEBUG_TYPE "Constraint solver overall"
|
|
#define JOIN2(X,Y) X##Y
|
|
STATISTIC(NumSolutionAttempts, "# of solution attempts");
|
|
STATISTIC(TotalNumTypeVariables, "# of type variables created");
|
|
|
|
#define CS_STATISTIC(Name, Description) \
|
|
STATISTIC(Overall##Name, Description);
|
|
#include "ConstraintSolverStats.def"
|
|
|
|
#undef DEBUG_TYPE
|
|
#define DEBUG_TYPE "Constraint solver largest system"
|
|
#define CS_STATISTIC(Name, Description) \
|
|
STATISTIC(Largest##Name, Description);
|
|
#include "ConstraintSolverStats.def"
|
|
STATISTIC(LargestSolutionAttemptNumber, "# of the largest solution attempt");
|
|
|
|
TypeVariableType *ConstraintSystem::createTypeVariable(
|
|
ConstraintLocator *locator,
|
|
unsigned options) {
|
|
++TotalNumTypeVariables;
|
|
auto tv = TypeVariableType::getNew(TC.Context, assignTypeVariableID(),
|
|
locator, options);
|
|
addTypeVariable(tv);
|
|
return tv;
|
|
}
|
|
|
|
/// \brief Check whether the given type can be used as a binding for the given
|
|
/// type variable.
|
|
///
|
|
/// \returns the type to bind to, if the binding is okay.
|
|
static Optional<Type> checkTypeOfBinding(ConstraintSystem &cs,
|
|
TypeVariableType *typeVar, Type type,
|
|
bool *isNilLiteral = nullptr) {
|
|
if (!type)
|
|
return None;
|
|
|
|
// Simplify the type.
|
|
type = cs.simplifyType(type);
|
|
|
|
// If the type references the type variable, don't permit the binding.
|
|
SmallVector<TypeVariableType *, 4> referencedTypeVars;
|
|
type->getTypeVariables(referencedTypeVars);
|
|
if (count(referencedTypeVars, typeVar))
|
|
return None;
|
|
|
|
// If the type is a type variable itself, don't permit the binding.
|
|
if (auto bindingTypeVar = type->getRValueType()->getAs<TypeVariableType>()) {
|
|
if (isNilLiteral) {
|
|
*isNilLiteral = false;
|
|
|
|
// Look for a literal-conformance constraint on the type variable.
|
|
SmallVector<Constraint *, 8> constraints;
|
|
cs.getConstraintGraph().gatherConstraints(
|
|
bindingTypeVar, constraints,
|
|
ConstraintGraph::GatheringKind::EquivalenceClass);
|
|
for (auto constraint : constraints) {
|
|
if (constraint->getKind() == ConstraintKind::LiteralConformsTo &&
|
|
constraint->getProtocol()->isSpecificProtocol(
|
|
KnownProtocolKind::ExpressibleByNilLiteral) &&
|
|
cs.simplifyType(constraint->getFirstType())
|
|
->isEqual(bindingTypeVar)) {
|
|
*isNilLiteral = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return None;
|
|
}
|
|
|
|
// Okay, allow the binding (with the simplified type).
|
|
return type;
|
|
}
|
|
|
|
Solution ConstraintSystem::finalize(
|
|
FreeTypeVariableBinding allowFreeTypeVariables) {
|
|
// Create the solution.
|
|
Solution solution(*this, CurrentScore);
|
|
|
|
// Update the best score we've seen so far.
|
|
if (solverState) {
|
|
assert(!solverState->BestScore || CurrentScore <= *solverState->BestScore);
|
|
solverState->BestScore = CurrentScore;
|
|
}
|
|
|
|
// For any of the type variables that has no associated fixed type, assign a
|
|
// fresh generic type parameters.
|
|
// FIXME: We could gather the requirements on these as well.
|
|
unsigned index = 0;
|
|
for (auto tv : TypeVariables) {
|
|
if (getFixedType(tv))
|
|
continue;
|
|
|
|
switch (allowFreeTypeVariables) {
|
|
case FreeTypeVariableBinding::Disallow:
|
|
llvm_unreachable("Solver left free type variables");
|
|
|
|
case FreeTypeVariableBinding::Allow:
|
|
break;
|
|
|
|
case FreeTypeVariableBinding::GenericParameters:
|
|
assignFixedType(tv, GenericTypeParamType::get(0, index++, TC.Context));
|
|
break;
|
|
|
|
case FreeTypeVariableBinding::UnresolvedType:
|
|
assignFixedType(tv, TC.Context.TheUnresolvedType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// For each of the type variables, get its fixed type.
|
|
for (auto tv : TypeVariables) {
|
|
solution.typeBindings[tv] = simplifyType(tv)->reconstituteSugar(false);
|
|
}
|
|
|
|
// For each of the overload sets, get its overload choice.
|
|
for (auto resolved = resolvedOverloadSets;
|
|
resolved; resolved = resolved->Previous) {
|
|
solution.overloadChoices[resolved->Locator]
|
|
= { resolved->Choice, resolved->OpenedFullType, resolved->ImpliedType };
|
|
}
|
|
|
|
// For each of the constraint restrictions, record it with simplified,
|
|
// canonical types.
|
|
if (solverState) {
|
|
for (auto &restriction : ConstraintRestrictions) {
|
|
using std::get;
|
|
CanType first = simplifyType(get<0>(restriction))->getCanonicalType();
|
|
CanType second = simplifyType(get<1>(restriction))->getCanonicalType();
|
|
solution.ConstraintRestrictions[{first, second}] = get<2>(restriction);
|
|
}
|
|
}
|
|
|
|
// For each of the fixes, record it as an operation on the affected
|
|
// expression.
|
|
unsigned firstFixIndex = 0;
|
|
if (solverState && solverState->PartialSolutionScope) {
|
|
firstFixIndex = solverState->PartialSolutionScope->numFixes;
|
|
}
|
|
solution.Fixes.append(Fixes.begin() + firstFixIndex, Fixes.end());
|
|
|
|
// Remember all the disjunction choices we made.
|
|
for (auto &choice : DisjunctionChoices) {
|
|
// We shouldn't ever register disjunction choices multiple times,
|
|
// but saving and re-applying solutions can cause us to get
|
|
// multiple entries. We should use an optimized PartialSolution
|
|
// structure for that use case, which would optimize a lot of
|
|
// stuff here.
|
|
assert(!solution.DisjunctionChoices.count(choice.first) ||
|
|
solution.DisjunctionChoices[choice.first] == choice.second);
|
|
solution.DisjunctionChoices.insert(choice);
|
|
}
|
|
|
|
// Remember the opened types.
|
|
for (const auto &opened : OpenedTypes) {
|
|
// We shouldn't ever register opened types multiple times,
|
|
// but saving and re-applying solutions can cause us to get
|
|
// multiple entries. We should use an optimized PartialSolution
|
|
// structure for that use case, which would optimize a lot of
|
|
// stuff here.
|
|
assert((solution.OpenedTypes.count(opened.first) == 0 ||
|
|
solution.OpenedTypes[opened.first] == opened.second)
|
|
&& "Already recorded");
|
|
solution.OpenedTypes.insert(opened);
|
|
}
|
|
|
|
// Remember the opened existential types.
|
|
for (const auto &openedExistential : OpenedExistentialTypes) {
|
|
assert(solution.OpenedExistentialTypes.count(openedExistential.first) == 0||
|
|
solution.OpenedExistentialTypes[openedExistential.first]
|
|
== openedExistential.second &&
|
|
"Already recorded");
|
|
solution.OpenedExistentialTypes.insert(openedExistential);
|
|
}
|
|
|
|
// Remember the defaulted type variables.
|
|
solution.DefaultedConstraints.insert(DefaultedConstraints.begin(),
|
|
DefaultedConstraints.end());
|
|
|
|
return solution;
|
|
}
|
|
|
|
void ConstraintSystem::applySolution(const Solution &solution) {
|
|
// Update the score.
|
|
CurrentScore += solution.getFixedScore();
|
|
|
|
// Assign fixed types to the type variables solved by this solution.
|
|
llvm::SmallPtrSet<TypeVariableType *, 4>
|
|
knownTypeVariables(TypeVariables.begin(), TypeVariables.end());
|
|
for (auto binding : solution.typeBindings) {
|
|
// If we haven't seen this type variable before, record it now.
|
|
if (knownTypeVariables.insert(binding.first).second)
|
|
TypeVariables.push_back(binding.first);
|
|
|
|
// If we don't already have a fixed type for this type variable,
|
|
// assign the fixed type from the solution.
|
|
if (!getFixedType(binding.first) && !binding.second->hasTypeVariable())
|
|
assignFixedType(binding.first, binding.second, /*updateState=*/false);
|
|
}
|
|
|
|
// Register overload choices.
|
|
// FIXME: Copy these directly into some kind of partial solution?
|
|
for (auto overload : solution.overloadChoices) {
|
|
resolvedOverloadSets
|
|
= new (*this) ResolvedOverloadSetListItem{resolvedOverloadSets,
|
|
Type(),
|
|
overload.second.choice,
|
|
overload.first,
|
|
overload.second.openedFullType,
|
|
overload.second.openedType};
|
|
}
|
|
|
|
// Register constraint restrictions.
|
|
// FIXME: Copy these directly into some kind of partial solution?
|
|
for (auto restriction : solution.ConstraintRestrictions) {
|
|
ConstraintRestrictions.push_back(
|
|
std::make_tuple(restriction.first.first, restriction.first.second,
|
|
restriction.second));
|
|
}
|
|
|
|
// Register the solution's disjunction choices.
|
|
for (auto &choice : solution.DisjunctionChoices) {
|
|
DisjunctionChoices.push_back(choice);
|
|
}
|
|
|
|
// Register the solution's opened types.
|
|
for (const auto &opened : solution.OpenedTypes) {
|
|
OpenedTypes.push_back(opened);
|
|
}
|
|
|
|
// Register the solution's opened existential types.
|
|
for (const auto &openedExistential : solution.OpenedExistentialTypes) {
|
|
OpenedExistentialTypes.push_back(openedExistential);
|
|
}
|
|
|
|
// Register the defaulted type variables.
|
|
DefaultedConstraints.append(solution.DefaultedConstraints.begin(),
|
|
solution.DefaultedConstraints.end());
|
|
|
|
// Register any fixes produced along this path.
|
|
Fixes.append(solution.Fixes.begin(), solution.Fixes.end());
|
|
}
|
|
|
|
/// \brief Restore the type variable bindings to what they were before
|
|
/// we attempted to solve this constraint system.
|
|
void ConstraintSystem::restoreTypeVariableBindings(unsigned numBindings) {
|
|
auto &savedBindings = *getSavedBindings();
|
|
std::for_each(savedBindings.rbegin(), savedBindings.rbegin() + numBindings,
|
|
[](SavedTypeVariableBinding &saved) {
|
|
saved.restore();
|
|
});
|
|
savedBindings.erase(savedBindings.end() - numBindings,
|
|
savedBindings.end());
|
|
}
|
|
|
|
/// \brief Enumerates all of the 'direct' supertypes of the given type.
|
|
///
|
|
/// The direct supertype S of a type T is a supertype of T (e.g., T < S)
|
|
/// such that there is no type U where T < U and U < S.
|
|
static SmallVector<Type, 4>
|
|
enumerateDirectSupertypes(TypeChecker &tc, Type type) {
|
|
SmallVector<Type, 4> result;
|
|
|
|
if (auto tupleTy = type->getAs<TupleType>()) {
|
|
// A tuple that can be constructed from a scalar has a value of that
|
|
// scalar type as its supertype.
|
|
// FIXME: There is a way more general property here, where we can drop
|
|
// one label from the tuple, maintaining the rest.
|
|
int scalarIdx = tupleTy->getElementForScalarInit();
|
|
if (scalarIdx >= 0) {
|
|
auto &elt = tupleTy->getElement(scalarIdx);
|
|
if (elt.isVararg()) // FIXME: Should we keep the name?
|
|
result.push_back(elt.getVarargBaseTy());
|
|
else if (elt.hasName())
|
|
result.push_back(elt.getType());
|
|
}
|
|
}
|
|
|
|
if (type->mayHaveSuperclass()) {
|
|
// FIXME: Can also weaken to the set of protocol constraints, but only
|
|
// if there are any protocols that the type conforms to but the superclass
|
|
// does not.
|
|
|
|
// If there is a superclass, it is a direct supertype.
|
|
if (auto superclass = tc.getSuperClassOf(type))
|
|
result.push_back(superclass);
|
|
}
|
|
|
|
if (auto lvalue = type->getAs<LValueType>())
|
|
result.push_back(lvalue->getObjectType());
|
|
if (auto iot = type->getAs<InOutType>())
|
|
result.push_back(iot->getObjectType());
|
|
|
|
// FIXME: lots of other cases to consider!
|
|
return result;
|
|
}
|
|
|
|
bool ConstraintSystem::simplify(bool ContinueAfterFailures) {
|
|
// While we have a constraint in the worklist, process it.
|
|
while (!ActiveConstraints.empty()) {
|
|
// Grab the next constraint from the worklist.
|
|
auto *constraint = &ActiveConstraints.front();
|
|
ActiveConstraints.pop_front();
|
|
assert(constraint->isActive() && "Worklist constraint is not active?");
|
|
|
|
// Simplify this constraint.
|
|
switch (simplifyConstraint(*constraint)) {
|
|
case SolutionKind::Error:
|
|
if (!failedConstraint) {
|
|
failedConstraint = constraint;
|
|
}
|
|
|
|
if (solverState)
|
|
solverState->retireConstraint(constraint);
|
|
|
|
CG.removeConstraint(constraint);
|
|
break;
|
|
|
|
case SolutionKind::Solved:
|
|
if (solverState) {
|
|
++solverState->NumSimplifiedConstraints;
|
|
|
|
// This constraint has already been solved; retire it.
|
|
solverState->retireConstraint(constraint);
|
|
}
|
|
|
|
// Remove the constraint from the constraint graph.
|
|
CG.removeConstraint(constraint);
|
|
break;
|
|
|
|
case SolutionKind::Unsolved:
|
|
if (solverState)
|
|
++solverState->NumUnsimplifiedConstraints;
|
|
|
|
InactiveConstraints.push_back(constraint);
|
|
break;
|
|
}
|
|
|
|
// This constraint is not active. We delay this operation until
|
|
// after simplification to avoid re-insertion.
|
|
constraint->setActive(false);
|
|
|
|
// Check whether a constraint failed. If so, we're done.
|
|
if (failedConstraint && !ContinueAfterFailures) {
|
|
return true;
|
|
}
|
|
|
|
// If the current score is worse than the best score we've seen so far,
|
|
// there's no point in continuing. So don't.
|
|
if (worseThanBestSolution()) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// \brief Truncate the given small vector to the given new size.
|
|
template<typename T>
|
|
void truncate(SmallVectorImpl<T> &vec, unsigned newSize) {
|
|
assert(newSize <= vec.size() && "Not a truncation!");
|
|
vec.erase(vec.begin() + newSize, vec.end());
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
ConstraintSystem::SolverState::SolverState(ConstraintSystem &cs) : CS(cs) {
|
|
assert(!CS.solverState &&
|
|
"Constraint system should not already have solver state!");
|
|
CS.solverState = this;
|
|
|
|
++NumSolutionAttempts;
|
|
SolutionAttempt = NumSolutionAttempts;
|
|
|
|
// If we're supposed to debug a specific constraint solver attempt,
|
|
// turn on debugging now.
|
|
ASTContext &ctx = CS.getTypeChecker().Context;
|
|
LangOptions &langOpts = ctx.LangOpts;
|
|
OldDebugConstraintSolver = langOpts.DebugConstraintSolver;
|
|
if (langOpts.DebugConstraintSolverAttempt &&
|
|
langOpts.DebugConstraintSolverAttempt == SolutionAttempt) {
|
|
langOpts.DebugConstraintSolver = true;
|
|
llvm::raw_ostream &dbgOut = ctx.TypeCheckerDebug->getStream();
|
|
dbgOut << "---Constraint system #" << SolutionAttempt << "---\n";
|
|
CS.print(dbgOut);
|
|
}
|
|
}
|
|
|
|
ConstraintSystem::SolverState::~SolverState() {
|
|
assert((CS.solverState == this) &&
|
|
"Expected constraint system to have this solver state!");
|
|
CS.solverState = nullptr;
|
|
|
|
// Restore debugging state.
|
|
LangOptions &langOpts = CS.getTypeChecker().Context.LangOpts;
|
|
langOpts.DebugConstraintSolver = OldDebugConstraintSolver;
|
|
|
|
// Write our local statistics back to the overall statistics.
|
|
#define CS_STATISTIC(Name, Description) JOIN2(Overall,Name) += Name;
|
|
#include "ConstraintSolverStats.def"
|
|
|
|
// Update the "largest" statistics if this system is larger than the
|
|
// previous one.
|
|
// FIXME: This is not at all thread-safe.
|
|
if (NumStatesExplored > LargestNumStatesExplored.Value) {
|
|
LargestSolutionAttemptNumber.Value = SolutionAttempt-1;
|
|
++LargestSolutionAttemptNumber;
|
|
#define CS_STATISTIC(Name, Description) \
|
|
JOIN2(Largest,Name).Value = Name-1; \
|
|
++JOIN2(Largest,Name);
|
|
#include "ConstraintSolverStats.def"
|
|
}
|
|
}
|
|
|
|
ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs)
|
|
: cs(cs), CGScope(cs.CG)
|
|
{
|
|
++cs.solverState->depth;
|
|
|
|
resolvedOverloadSets = cs.resolvedOverloadSets;
|
|
numTypeVariables = cs.TypeVariables.size();
|
|
numSavedBindings = cs.solverState->savedBindings.size();
|
|
numConstraintRestrictions = cs.ConstraintRestrictions.size();
|
|
numFixes = cs.Fixes.size();
|
|
numDisjunctionChoices = cs.DisjunctionChoices.size();
|
|
numOpenedTypes = cs.OpenedTypes.size();
|
|
numOpenedExistentialTypes = cs.OpenedExistentialTypes.size();
|
|
numDefaultedConstraints = cs.DefaultedConstraints.size();
|
|
PreviousScore = cs.CurrentScore;
|
|
|
|
cs.solverState->registerScope(this);
|
|
assert(!cs.failedConstraint && "Unexpected failed constraint!");
|
|
|
|
++cs.solverState->NumStatesExplored;
|
|
}
|
|
|
|
ConstraintSystem::SolverScope::~SolverScope() {
|
|
--cs.solverState->depth;
|
|
|
|
// Erase the end of various lists.
|
|
cs.resolvedOverloadSets = resolvedOverloadSets;
|
|
truncate(cs.TypeVariables, numTypeVariables);
|
|
|
|
// Restore bindings.
|
|
cs.restoreTypeVariableBindings(cs.solverState->savedBindings.size() -
|
|
numSavedBindings);
|
|
|
|
// Move any remaining active constraints into the inactive list.
|
|
if (!cs.ActiveConstraints.empty()) {
|
|
for (auto &constraint : cs.ActiveConstraints) {
|
|
constraint.setActive(false);
|
|
}
|
|
cs.InactiveConstraints.splice(cs.InactiveConstraints.end(),
|
|
cs.ActiveConstraints);
|
|
}
|
|
|
|
// Rollback all of the changes done to constraints by the current scope,
|
|
// e.g. add retired constraints back to the circulation and remove generated
|
|
// constraints introduced by the current scope.
|
|
cs.solverState->rollback(this);
|
|
|
|
// Remove any constraint restrictions.
|
|
truncate(cs.ConstraintRestrictions, numConstraintRestrictions);
|
|
|
|
// Remove any fixes.
|
|
truncate(cs.Fixes, numFixes);
|
|
|
|
// Remove any disjunction choices.
|
|
truncate(cs.DisjunctionChoices, numDisjunctionChoices);
|
|
|
|
// Remove any opened types.
|
|
truncate(cs.OpenedTypes, numOpenedTypes);
|
|
|
|
// Remove any opened existential types.
|
|
truncate(cs.OpenedExistentialTypes, numOpenedExistentialTypes);
|
|
|
|
// Remove any defaulted type variables.
|
|
truncate(cs.DefaultedConstraints, numDefaultedConstraints);
|
|
|
|
// Reset the previous score.
|
|
cs.CurrentScore = PreviousScore;
|
|
|
|
// Clear out other "failed" state.
|
|
cs.failedConstraint = nullptr;
|
|
}
|
|
|
|
namespace {
|
|
/// The kind of bindings that are permitted.
|
|
enum class AllowedBindingKind : unsigned char {
|
|
/// Only the exact type.
|
|
Exact,
|
|
/// Supertypes of the specified type.
|
|
Supertypes,
|
|
/// Subtypes of the specified type.
|
|
Subtypes
|
|
};
|
|
|
|
/// The kind of literal binding found.
|
|
enum class LiteralBindingKind : unsigned char {
|
|
None,
|
|
Collection,
|
|
Float,
|
|
Atom,
|
|
};
|
|
|
|
/// A potential binding from the type variable to a particular type,
|
|
/// along with information that can be used to construct related
|
|
/// bindings, e.g., the supertypes of a given type.
|
|
struct PotentialBinding {
|
|
/// The type to which the type variable can be bound.
|
|
Type BindingType;
|
|
|
|
/// The kind of bindings permitted.
|
|
AllowedBindingKind Kind;
|
|
|
|
/// The defaulted protocol associated with this binding.
|
|
Optional<ProtocolDecl *> DefaultedProtocol;
|
|
|
|
/// If this is a binding that comes from a \c Defaultable constraint,
|
|
/// the locator of that constraint.
|
|
ConstraintLocator *DefaultableBinding = nullptr;
|
|
|
|
PotentialBinding(Type type, AllowedBindingKind kind,
|
|
Optional<ProtocolDecl *> defaultedProtocol = None,
|
|
ConstraintLocator *defaultableBinding = nullptr)
|
|
: BindingType(type), Kind(kind), DefaultedProtocol(defaultedProtocol),
|
|
DefaultableBinding(defaultableBinding) { }
|
|
|
|
bool isDefaultableBinding() const { return DefaultableBinding != nullptr; }
|
|
};
|
|
|
|
struct PotentialBindings {
|
|
/// The set of potential bindings.
|
|
SmallVector<PotentialBinding, 4> Bindings;
|
|
|
|
/// Whether this type variable is fully bound by one of its constraints.
|
|
bool FullyBound = false;
|
|
|
|
/// Whether the bindings of this type involve other type variables.
|
|
bool InvolvesTypeVariables = false;
|
|
|
|
/// Whether this type variable has literal bindings.
|
|
LiteralBindingKind LiteralBinding = LiteralBindingKind::None;
|
|
|
|
/// Whether this type variable is only bound above by existential types.
|
|
bool SubtypeOfExistentialType = false;
|
|
|
|
/// The number of defaultable bindings.
|
|
unsigned NumDefaultableBindings = 0;
|
|
|
|
/// Determine whether the set of bindings is non-empty.
|
|
explicit operator bool() const {
|
|
return !Bindings.empty();
|
|
}
|
|
|
|
/// Whether there are any non-defaultable bindings.
|
|
bool hasNonDefaultableBindings() const {
|
|
return Bindings.size() > NumDefaultableBindings;
|
|
}
|
|
|
|
/// Compare two sets of bindings, where \c x < y indicates that
|
|
/// \c x is a better set of bindings that \c y.
|
|
friend bool operator<(const PotentialBindings &x,
|
|
const PotentialBindings &y) {
|
|
return std::make_tuple(!x.hasNonDefaultableBindings(),
|
|
x.FullyBound,
|
|
x.SubtypeOfExistentialType,
|
|
static_cast<unsigned char>(x.LiteralBinding),
|
|
x.InvolvesTypeVariables,
|
|
-(x.Bindings.size() - x.NumDefaultableBindings))
|
|
< std::make_tuple(!y.hasNonDefaultableBindings(),
|
|
y.FullyBound,
|
|
y.SubtypeOfExistentialType,
|
|
static_cast<unsigned char>(y.LiteralBinding),
|
|
y.InvolvesTypeVariables,
|
|
-(y.Bindings.size() - y.NumDefaultableBindings));
|
|
}
|
|
|
|
void foundLiteralBinding(ProtocolDecl *proto) {
|
|
switch (*proto->getKnownProtocolKind()) {
|
|
case KnownProtocolKind::ExpressibleByDictionaryLiteral:
|
|
case KnownProtocolKind::ExpressibleByArrayLiteral:
|
|
case KnownProtocolKind::ExpressibleByStringInterpolation:
|
|
LiteralBinding = LiteralBindingKind::Collection;
|
|
break;
|
|
|
|
case KnownProtocolKind::ExpressibleByFloatLiteral:
|
|
LiteralBinding = LiteralBindingKind::Float;
|
|
break;
|
|
|
|
default:
|
|
if (LiteralBinding != LiteralBindingKind::Collection)
|
|
LiteralBinding = LiteralBindingKind::Atom;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void dump(TypeVariableType *typeVar, llvm::raw_ostream &out,
|
|
unsigned indent) const {
|
|
out.indent(indent);
|
|
out << "(";
|
|
if (typeVar)
|
|
out << "$T" << typeVar->getImpl().getID();
|
|
if (FullyBound)
|
|
out << " fully_bound";
|
|
if (SubtypeOfExistentialType)
|
|
out << " subtype_of_existential";
|
|
if (LiteralBinding != LiteralBindingKind::None)
|
|
out << " literal=" << static_cast<int>(LiteralBinding);
|
|
if (InvolvesTypeVariables)
|
|
out << " involves_type_vars";
|
|
if (NumDefaultableBindings > 0)
|
|
out << " defaultable_bindings=" << NumDefaultableBindings;
|
|
out << " bindings=";
|
|
interleave(Bindings, [&](const PotentialBinding &binding) {
|
|
auto type = binding.BindingType;
|
|
auto &ctx = type->getASTContext();
|
|
llvm::SaveAndRestore<bool>
|
|
debugConstraints(ctx.LangOpts.DebugConstraintSolver, true);
|
|
switch (binding.Kind) {
|
|
case AllowedBindingKind::Exact:
|
|
break;
|
|
|
|
case AllowedBindingKind::Subtypes:
|
|
out << "(subtypes of) ";
|
|
break;
|
|
|
|
case AllowedBindingKind::Supertypes:
|
|
out << "(supertypes of) ";
|
|
break;
|
|
}
|
|
if (binding.DefaultedProtocol)
|
|
out << "(default from " << (*binding.DefaultedProtocol)->getName()
|
|
<< ") ";
|
|
out << type.getString();
|
|
}, [&]() { out << " "; });
|
|
out << ")\n";
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// \brief Return whether a relational constraint between a type variable and a
|
|
/// trivial wrapper type (autoclosure, unary tuple) should result in the type
|
|
/// variable being potentially bound to the value type, as opposed to the
|
|
/// wrapper type.
|
|
static bool shouldBindToValueType(Constraint *constraint)
|
|
{
|
|
switch (constraint->getKind()) {
|
|
case ConstraintKind::OperatorArgumentConversion:
|
|
case ConstraintKind::OperatorArgumentTupleConversion:
|
|
case ConstraintKind::ArgumentConversion:
|
|
case ConstraintKind::ArgumentTupleConversion:
|
|
case ConstraintKind::Conversion:
|
|
case ConstraintKind::BridgingConversion:
|
|
case ConstraintKind::Subtype:
|
|
return true;
|
|
case ConstraintKind::Bind:
|
|
case ConstraintKind::Equal:
|
|
case ConstraintKind::BindParam:
|
|
case ConstraintKind::BindToPointerType:
|
|
case ConstraintKind::ConformsTo:
|
|
case ConstraintKind::LiteralConformsTo:
|
|
case ConstraintKind::CheckedCast:
|
|
case ConstraintKind::SelfObjectOfProtocol:
|
|
case ConstraintKind::ApplicableFunction:
|
|
case ConstraintKind::BindOverload:
|
|
case ConstraintKind::OptionalObject:
|
|
return false;
|
|
case ConstraintKind::DynamicTypeOf:
|
|
case ConstraintKind::EscapableFunctionOf:
|
|
case ConstraintKind::OpenedExistentialOf:
|
|
case ConstraintKind::KeyPath:
|
|
case ConstraintKind::KeyPathApplication:
|
|
case ConstraintKind::ValueMember:
|
|
case ConstraintKind::UnresolvedValueMember:
|
|
case ConstraintKind::Defaultable:
|
|
case ConstraintKind::Disjunction:
|
|
llvm_unreachable("shouldBindToValueType() may only be called on "
|
|
"relational constraints");
|
|
}
|
|
|
|
llvm_unreachable("Unhandled ConstraintKind in switch.");
|
|
}
|
|
|
|
/// Find the set of type variables that are inferable from the given type.
|
|
///
|
|
/// \param type The type to search.
|
|
/// \param typeVars Collects the type variables that are inferable from the
|
|
/// given type. This set is not cleared, so that multiple types can be explored
|
|
/// and introduce their results into the same set.
|
|
static void findInferableTypeVars(
|
|
Type type,
|
|
SmallPtrSetImpl<TypeVariableType *> &typeVars) {
|
|
type = type->getCanonicalType();
|
|
if (!type->hasTypeVariable()) return;
|
|
|
|
class Walker : public TypeWalker {
|
|
SmallPtrSetImpl<TypeVariableType *> &typeVars;
|
|
public:
|
|
explicit Walker(SmallPtrSetImpl<TypeVariableType *> &typeVars)
|
|
: typeVars(typeVars) { }
|
|
|
|
Action walkToTypePre(Type ty) override {
|
|
if (ty->is<DependentMemberType>())
|
|
return Action::SkipChildren;
|
|
|
|
if (auto typeVar = ty->getAs<TypeVariableType>())
|
|
typeVars.insert(typeVar);
|
|
return Action::Continue;
|
|
}
|
|
};
|
|
|
|
type.walk(Walker(typeVars));
|
|
}
|
|
|
|
/// \brief Retrieve the set of potential type bindings for the given
|
|
/// representative type variable, along with flags indicating whether
|
|
/// those types should be opened.
|
|
static PotentialBindings getPotentialBindings(ConstraintSystem &cs,
|
|
TypeVariableType *typeVar) {
|
|
assert(typeVar->getImpl().getRepresentative(nullptr) == typeVar &&
|
|
"not a representative");
|
|
assert(!typeVar->getImpl().getFixedType(nullptr) && "has a fixed type");
|
|
|
|
// Gather the constraints associated with this type variable.
|
|
SmallVector<Constraint *, 8> constraints;
|
|
llvm::SmallPtrSet<Constraint *, 4> visitedConstraints;
|
|
cs.getConstraintGraph().gatherConstraints(
|
|
typeVar, constraints,
|
|
ConstraintGraph::GatheringKind::EquivalenceClass);
|
|
|
|
PotentialBindings result;
|
|
Optional<unsigned> lastSupertypeIndex;
|
|
|
|
// Local function to add a potential binding to the list of bindings,
|
|
// coalescing supertype bounds when we are able to compute the meet.
|
|
auto addPotentialBinding = [&](PotentialBinding binding,
|
|
bool allowJoinMeet = true) {
|
|
// If this is a non-defaulted supertype binding, check whether we can
|
|
// combine it with another supertype binding by computing the 'join' of the
|
|
// types.
|
|
if (binding.Kind == AllowedBindingKind::Supertypes &&
|
|
!binding.BindingType->hasTypeVariable() &&
|
|
!binding.DefaultedProtocol &&
|
|
!binding.isDefaultableBinding() &&
|
|
allowJoinMeet) {
|
|
if (lastSupertypeIndex) {
|
|
// Can we compute a join?
|
|
auto &lastBinding = result.Bindings[*lastSupertypeIndex];
|
|
if (auto meet =
|
|
Type::join(lastBinding.BindingType, binding.BindingType)) {
|
|
// Replace the last supertype binding with the join. We're done.
|
|
lastBinding.BindingType = meet;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Record this as the most recent supertype index.
|
|
lastSupertypeIndex = result.Bindings.size();
|
|
}
|
|
|
|
result.Bindings.push_back(std::move(binding));
|
|
};
|
|
|
|
// Consider each of the constraints related to this type variable.
|
|
llvm::SmallPtrSet<CanType, 4> exactTypes;
|
|
llvm::SmallPtrSet<ProtocolDecl *, 4> literalProtocols;
|
|
SmallVector<Constraint *, 2> defaultableConstraints;
|
|
bool addOptionalSupertypeBindings = false;
|
|
auto &tc = cs.getTypeChecker();
|
|
bool hasNonDependentMemberRelationalConstraints = false;
|
|
bool hasDependentMemberRelationalConstraints = false;
|
|
for (auto constraint : constraints) {
|
|
// Only visit each constraint once.
|
|
if (!visitedConstraints.insert(constraint).second)
|
|
continue;
|
|
|
|
switch (constraint->getKind()) {
|
|
case ConstraintKind::Bind:
|
|
case ConstraintKind::Equal:
|
|
case ConstraintKind::BindParam:
|
|
case ConstraintKind::BindToPointerType:
|
|
case ConstraintKind::Subtype:
|
|
case ConstraintKind::Conversion:
|
|
case ConstraintKind::ArgumentConversion:
|
|
case ConstraintKind::ArgumentTupleConversion:
|
|
case ConstraintKind::OperatorArgumentTupleConversion:
|
|
case ConstraintKind::OperatorArgumentConversion:
|
|
case ConstraintKind::OptionalObject:
|
|
// Relational constraints: break out to look for types above/below.
|
|
break;
|
|
|
|
case ConstraintKind::DynamicTypeOf: {
|
|
auto otherTy = constraint->getSecondType();
|
|
if (constraint->getSecondType()->isEqual(typeVar))
|
|
otherTy = constraint->getFirstType();
|
|
|
|
auto simplified = cs.simplifyType(otherTy);
|
|
if (simplified->hasTypeVariable())
|
|
result.InvolvesTypeVariables = true;
|
|
continue;
|
|
}
|
|
|
|
case ConstraintKind::BridgingConversion:
|
|
case ConstraintKind::CheckedCast:
|
|
case ConstraintKind::EscapableFunctionOf:
|
|
case ConstraintKind::OpenedExistentialOf:
|
|
case ConstraintKind::KeyPath:
|
|
case ConstraintKind::KeyPathApplication:
|
|
// FIXME: check for other type variables?
|
|
// Constraints from which we can't do anything.
|
|
continue;
|
|
|
|
case ConstraintKind::Defaultable:
|
|
// Do these in a separate pass.
|
|
if (cs.getFixedTypeRecursive(constraint->getFirstType(), true)
|
|
->getAs<TypeVariableType>() == typeVar) {
|
|
defaultableConstraints.push_back(constraint);
|
|
hasNonDependentMemberRelationalConstraints = true;
|
|
}
|
|
continue;
|
|
|
|
case ConstraintKind::Disjunction:
|
|
// FIXME: Recurse into these constraints to see whether this
|
|
// type variable is fully bound by any of them.
|
|
result.InvolvesTypeVariables = true;
|
|
continue;
|
|
|
|
case ConstraintKind::ConformsTo:
|
|
case ConstraintKind::SelfObjectOfProtocol:
|
|
// Swift 3 allowed the use of default types for normal conformances
|
|
// to expressible-by-literal protocols.
|
|
if (tc.Context.LangOpts.EffectiveLanguageVersion[0] >= 4)
|
|
continue;
|
|
|
|
if (!constraint->getSecondType()->is<ProtocolType>())
|
|
continue;
|
|
|
|
LLVM_FALLTHROUGH;
|
|
|
|
case ConstraintKind::LiteralConformsTo: {
|
|
// If there is a 'nil' literal constraint, we might need optional
|
|
// supertype bindings.
|
|
if (constraint->getProtocol()->isSpecificProtocol(
|
|
KnownProtocolKind::ExpressibleByNilLiteral))
|
|
addOptionalSupertypeBindings = true;
|
|
|
|
// If there is a default literal type for this protocol, it's a
|
|
// potential binding.
|
|
auto defaultType = tc.getDefaultType(constraint->getProtocol(), cs.DC);
|
|
if (!defaultType)
|
|
continue;
|
|
|
|
// Note that we have a literal constraint with this protocol.
|
|
literalProtocols.insert(constraint->getProtocol());
|
|
hasNonDependentMemberRelationalConstraints = true;
|
|
|
|
// Handle unspecialized types directly.
|
|
if (!defaultType->hasUnboundGenericType()) {
|
|
if (!exactTypes.insert(defaultType->getCanonicalType()).second)
|
|
continue;
|
|
|
|
result.foundLiteralBinding(constraint->getProtocol());
|
|
addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
|
|
constraint->getProtocol()});
|
|
continue;
|
|
}
|
|
|
|
// For generic literal types, check whether we already have a
|
|
// specialization of this generic within our list.
|
|
// FIXME: This assumes that, e.g., the default literal
|
|
// int/float/char/string types are never generic.
|
|
auto nominal = defaultType->getAnyNominal();
|
|
if (!nominal)
|
|
continue;
|
|
|
|
bool matched = false;
|
|
for (auto exactType : exactTypes) {
|
|
if (auto exactNominal = exactType->getAnyNominal()) {
|
|
// FIXME: Check parents?
|
|
if (nominal == exactNominal) {
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!matched) {
|
|
result.foundLiteralBinding(constraint->getProtocol());
|
|
exactTypes.insert(defaultType->getCanonicalType());
|
|
addPotentialBinding({defaultType, AllowedBindingKind::Subtypes,
|
|
constraint->getProtocol()});
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
case ConstraintKind::ApplicableFunction:
|
|
case ConstraintKind::BindOverload: {
|
|
if (result.FullyBound && result.InvolvesTypeVariables) continue;
|
|
|
|
// If this variable is in the left-hand side, it is fully bound.
|
|
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
|
findInferableTypeVars(cs.simplifyType(constraint->getFirstType()),
|
|
typeVars);
|
|
if (typeVars.count(typeVar))
|
|
result.FullyBound = true;
|
|
|
|
if (result.InvolvesTypeVariables) continue;
|
|
|
|
// If this and another type variable occur, this result involves
|
|
// type variables.
|
|
findInferableTypeVars(cs.simplifyType(constraint->getSecondType()),
|
|
typeVars);
|
|
if (typeVars.size() > 1 && typeVars.count(typeVar))
|
|
result.InvolvesTypeVariables = true;
|
|
continue;
|
|
}
|
|
|
|
case ConstraintKind::ValueMember:
|
|
case ConstraintKind::UnresolvedValueMember:
|
|
// If our type variable shows up in the base type, there's
|
|
// nothing to do.
|
|
// FIXME: Can we avoid simplification here?
|
|
if (ConstraintSystem::typeVarOccursInType(
|
|
typeVar,
|
|
cs.simplifyType(constraint->getFirstType()),
|
|
&result.InvolvesTypeVariables)) {
|
|
continue;
|
|
}
|
|
|
|
// If the type variable is in the list of member type
|
|
// variables, it is fully bound.
|
|
// FIXME: Can we avoid simplification here?
|
|
if (ConstraintSystem::typeVarOccursInType(
|
|
typeVar,
|
|
cs.simplifyType(constraint->getSecondType()),
|
|
&result.InvolvesTypeVariables)) {
|
|
result.FullyBound = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Handle relational constraints.
|
|
assert(constraint->getClassification()
|
|
== ConstraintClassification::Relational &&
|
|
"only relational constraints handled here");
|
|
|
|
auto first = cs.simplifyType(constraint->getFirstType());
|
|
auto second = cs.simplifyType(constraint->getSecondType());
|
|
|
|
if (first->is<TypeVariableType>() && first->isEqual(second))
|
|
continue;
|
|
|
|
Type type;
|
|
AllowedBindingKind kind;
|
|
if (first->getAs<TypeVariableType>() == typeVar) {
|
|
// Upper bound for this type variable.
|
|
type = second;
|
|
kind = AllowedBindingKind::Subtypes;
|
|
} else if (second->getAs<TypeVariableType>() == typeVar) {
|
|
// Lower bound for this type variable.
|
|
type = first;
|
|
kind = AllowedBindingKind::Supertypes;
|
|
} else {
|
|
// Can't infer anything.
|
|
if (result.InvolvesTypeVariables) continue;
|
|
|
|
// Check whether both this type and another type variable are
|
|
// inferable.
|
|
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
|
findInferableTypeVars(first, typeVars);
|
|
findInferableTypeVars(second, typeVars);
|
|
if (typeVars.size() > 1 && typeVars.count(typeVar))
|
|
result.InvolvesTypeVariables = true;
|
|
continue;
|
|
}
|
|
|
|
// If the type we'd be binding to is a dependent member, don't try to
|
|
// resolve this type variable yet.
|
|
if (type->is<DependentMemberType>()) {
|
|
if (!ConstraintSystem::typeVarOccursInType(
|
|
typeVar, type, &result.InvolvesTypeVariables)) {
|
|
hasDependentMemberRelationalConstraints = true;
|
|
}
|
|
continue;
|
|
}
|
|
hasNonDependentMemberRelationalConstraints = true;
|
|
|
|
// Check whether we can perform this binding.
|
|
// FIXME: this has a super-inefficient extraneous simplifyType() in it.
|
|
bool isNilLiteral = false;
|
|
bool *isNilLiteralPtr = nullptr;
|
|
if (!addOptionalSupertypeBindings && kind == AllowedBindingKind::Supertypes)
|
|
isNilLiteralPtr = &isNilLiteral;
|
|
if (auto boundType = checkTypeOfBinding(cs, typeVar, type,
|
|
isNilLiteralPtr)) {
|
|
type = *boundType;
|
|
if (type->hasTypeVariable())
|
|
result.InvolvesTypeVariables = true;
|
|
} else {
|
|
// If the bound is a 'nil' literal type, add optional supertype bindings.
|
|
if (isNilLiteral) {
|
|
addOptionalSupertypeBindings = true;
|
|
continue;
|
|
}
|
|
|
|
// If the other side of an argument conversion is also a type
|
|
// variable, we shouldn't allow that to block us from attempting
|
|
// bindings if it is the only constraint involving other type
|
|
// variables. Doing this allows us to attempt a binding much
|
|
// earlier in solving which can result in backing out of
|
|
// solutions that cannot work due to conformance constraints.
|
|
if (constraint->getKind() ==
|
|
ConstraintKind::OperatorArgumentTupleConversion ||
|
|
constraint->getKind() == ConstraintKind::OperatorArgumentConversion)
|
|
continue;
|
|
|
|
result.InvolvesTypeVariables = true;
|
|
continue;
|
|
}
|
|
|
|
// Don't deduce autoclosure types or single-element, non-variadic
|
|
// tuples.
|
|
if (shouldBindToValueType(constraint)) {
|
|
if (auto funcTy = type->getAs<FunctionType>()) {
|
|
if (funcTy->isAutoClosure())
|
|
type = funcTy->getResult();
|
|
}
|
|
|
|
type = type->getWithoutImmediateLabel();
|
|
}
|
|
|
|
// Don't deduce IUO types.
|
|
Type alternateType;
|
|
bool adjustedIUO = false;
|
|
if (kind == AllowedBindingKind::Supertypes &&
|
|
constraint->getKind() >= ConstraintKind::Conversion &&
|
|
constraint->getKind() <= ConstraintKind::OperatorArgumentConversion) {
|
|
auto innerType = type->getLValueOrInOutObjectType();
|
|
if (auto objectType =
|
|
cs.lookThroughImplicitlyUnwrappedOptionalType(innerType)) {
|
|
type = OptionalType::get(objectType);
|
|
alternateType = objectType;
|
|
adjustedIUO = true;
|
|
}
|
|
}
|
|
|
|
// Make sure we aren't trying to equate type variables with different
|
|
// lvalue-binding rules.
|
|
if (auto otherTypeVar = type->getAs<TypeVariableType>()) {
|
|
if (typeVar->getImpl().canBindToLValue() !=
|
|
otherTypeVar->getImpl().canBindToLValue())
|
|
continue;
|
|
}
|
|
|
|
// BindParam constraints are not reflexive and must be treated specially.
|
|
if (constraint->getKind() == ConstraintKind::BindParam) {
|
|
if (kind == AllowedBindingKind::Subtypes) {
|
|
if (auto *lvt = type->getAs<LValueType>()) {
|
|
type = InOutType::get(lvt->getObjectType());
|
|
}
|
|
} else if (kind == AllowedBindingKind::Supertypes) {
|
|
if (auto *iot = type->getAs<InOutType>()) {
|
|
type = LValueType::get(iot->getObjectType());
|
|
}
|
|
}
|
|
kind = AllowedBindingKind::Exact;
|
|
}
|
|
|
|
if (exactTypes.insert(type->getCanonicalType()).second)
|
|
addPotentialBinding({type, kind, None}, /*allowJoinMeet=*/!adjustedIUO);
|
|
if (alternateType &&
|
|
exactTypes.insert(alternateType->getCanonicalType()).second)
|
|
addPotentialBinding({alternateType, kind, None}, /*allowJoinMeet=*/false);
|
|
}
|
|
|
|
// If we have any literal constraints, check whether there is already a
|
|
// binding that provides a type that conforms to that literal protocol. In
|
|
// such cases, remove the default binding suggestion because the existing
|
|
// suggestion is better.
|
|
if (!literalProtocols.empty()) {
|
|
SmallPtrSet<ProtocolDecl *, 5> coveredLiteralProtocols;
|
|
for (auto &binding : result.Bindings) {
|
|
// Skip defaulted-protocol constraints.
|
|
if (binding.DefaultedProtocol)
|
|
continue;
|
|
|
|
Type testType;
|
|
switch (binding.Kind) {
|
|
case AllowedBindingKind::Exact:
|
|
testType = binding.BindingType;
|
|
break;
|
|
|
|
case AllowedBindingKind::Subtypes:
|
|
case AllowedBindingKind::Supertypes:
|
|
testType = binding.BindingType->getRValueType();
|
|
break;
|
|
}
|
|
|
|
// Check each non-covered literal protocol to determine which ones
|
|
bool updatedBindingType = false;
|
|
for (auto proto : literalProtocols) {
|
|
do {
|
|
// If the type conforms to this protocol, we're covered.
|
|
if (tc.conformsToProtocol(testType, proto, cs.DC,
|
|
ConformanceCheckFlags::InExpression)) {
|
|
coveredLiteralProtocols.insert(proto);
|
|
break;
|
|
}
|
|
|
|
// If we're allowed to bind to subtypes, look through optionals.
|
|
// FIXME: This is really crappy special case of computing a reasonable
|
|
// result based on the given constraints.
|
|
if (binding.Kind == AllowedBindingKind::Subtypes) {
|
|
if (auto objTy = testType->getAnyOptionalObjectType()) {
|
|
updatedBindingType = true;
|
|
testType = objTy;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
updatedBindingType = false;
|
|
break;
|
|
} while (true);
|
|
}
|
|
|
|
if (updatedBindingType)
|
|
binding.BindingType = testType;
|
|
}
|
|
|
|
// For any literal type that has been covered, remove the default literal
|
|
// type.
|
|
if (!coveredLiteralProtocols.empty()) {
|
|
result.Bindings.erase(
|
|
std::remove_if(result.Bindings.begin(),
|
|
result.Bindings.end(),
|
|
[&](PotentialBinding &binding) {
|
|
return binding.DefaultedProtocol &&
|
|
coveredLiteralProtocols.count(*binding.DefaultedProtocol) > 0;
|
|
}),
|
|
result.Bindings.end());
|
|
}
|
|
}
|
|
|
|
/// Add defaultable constraints last.
|
|
for (auto constraint : defaultableConstraints) {
|
|
Type type = constraint->getSecondType();
|
|
if (!exactTypes.insert(type->getCanonicalType()).second)
|
|
continue;
|
|
|
|
++result.NumDefaultableBindings;
|
|
addPotentialBinding({type, AllowedBindingKind::Exact, None,
|
|
constraint->getLocator()});
|
|
}
|
|
|
|
// Determine if the bindings only constrain the type variable from above with
|
|
// an existential type; such a binding is not very helpful because it's
|
|
// impossible to enumerate the existential type's subtypes.
|
|
result.SubtypeOfExistentialType =
|
|
std::all_of(result.Bindings.begin(), result.Bindings.end(),
|
|
[](const PotentialBinding &binding) {
|
|
return binding.BindingType->isExistentialType() &&
|
|
binding.Kind == AllowedBindingKind::Subtypes;
|
|
});
|
|
|
|
// If we're supposed to add optional supertype bindings, do so now.
|
|
if (addOptionalSupertypeBindings) {
|
|
for (unsigned i : indices(result.Bindings)) {
|
|
auto &binding = result.Bindings[i];
|
|
bool wrapInOptional = false;
|
|
|
|
if (binding.Kind == AllowedBindingKind::Supertypes) {
|
|
// If the type doesn't conform to ExpressibleByNilLiteral,
|
|
// produce an optional of that type as a potential binding. We
|
|
// overwrite the binding in place because the non-optional type
|
|
// will fail to type-check against the nil-literal conformance.
|
|
auto nominalBindingDecl =
|
|
binding.BindingType->getRValueType()->getAnyNominal();
|
|
bool conformsToExprByNilLiteral = false;
|
|
if (nominalBindingDecl) {
|
|
SmallVector<ProtocolConformance *, 2> conformances;
|
|
conformsToExprByNilLiteral = nominalBindingDecl->lookupConformance(
|
|
cs.DC->getParentModule(),
|
|
cs.getASTContext().getProtocol(
|
|
KnownProtocolKind::ExpressibleByNilLiteral),
|
|
conformances);
|
|
}
|
|
wrapInOptional = !conformsToExprByNilLiteral;
|
|
} else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) {
|
|
wrapInOptional = true;
|
|
}
|
|
|
|
if (wrapInOptional) {
|
|
binding.BindingType = OptionalType::get(binding.BindingType);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there were both dependent-member and non-dependent-member relational
|
|
// constraints, consider this "fully bound"; we don't want to touch it.
|
|
if (hasDependentMemberRelationalConstraints) {
|
|
if (hasNonDependentMemberRelationalConstraints)
|
|
result.FullyBound = true;
|
|
else
|
|
result.Bindings.clear();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// \brief Try each of the given type variable bindings to find solutions
|
|
/// to the given constraint system.
|
|
///
|
|
/// \param cs The constraint system we're solving in.
|
|
/// \param depth The depth of the solution stack.
|
|
/// \param typeVar The type variable we're binding.
|
|
/// \param bindings The initial set of bindings to explore.
|
|
/// \param solutions The set of solutions.
|
|
///
|
|
/// \returns true if there are no solutions.
|
|
static bool tryTypeVariableBindings(
|
|
ConstraintSystem &cs,
|
|
unsigned depth,
|
|
TypeVariableType *typeVar,
|
|
ArrayRef<PotentialBinding> bindings,
|
|
SmallVectorImpl<Solution> &solutions,
|
|
FreeTypeVariableBinding allowFreeTypeVariables) {
|
|
bool anySolved = false;
|
|
llvm::SmallPtrSet<CanType, 4> exploredTypes;
|
|
llvm::SmallPtrSet<TypeBase *, 4> boundTypes;
|
|
|
|
SmallVector<PotentialBinding, 4> storedBindings;
|
|
auto &tc = cs.getTypeChecker();
|
|
++cs.solverState->NumTypeVariablesBound;
|
|
|
|
// If we've already explored a lot of potential solutions, bail.
|
|
if (cs.getExpressionTooComplex(solutions))
|
|
return true;
|
|
|
|
for (unsigned tryCount = 0; !anySolved && !bindings.empty(); ++tryCount) {
|
|
// Try each of the bindings in turn.
|
|
++cs.solverState->NumTypeVariableBindings;
|
|
bool sawFirstLiteralConstraint = false;
|
|
|
|
if (tc.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = cs.getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(depth * 2) << "Active bindings: ";
|
|
|
|
for (auto binding : bindings) {
|
|
log << typeVar->getString() << " := "
|
|
<< binding.BindingType->getString() << " ";
|
|
}
|
|
|
|
log <<"\n";
|
|
}
|
|
|
|
for (const auto &binding : bindings) {
|
|
// If this is a defaultable binding and we have found any solutions,
|
|
// don't explore the default binding.
|
|
if (binding.isDefaultableBinding() && anySolved)
|
|
continue;
|
|
|
|
auto type = binding.BindingType;
|
|
|
|
// If the type variable can't bind to an lvalue, make sure the
|
|
// type we pick isn't an lvalue.
|
|
if (!typeVar->getImpl().canBindToLValue())
|
|
type = type->getRValueType();
|
|
|
|
// Remove parentheses. They're insignificant here.
|
|
type = type->getWithoutParens();
|
|
|
|
// If we've already tried this binding, move on.
|
|
if (!boundTypes.insert(type.getPointer()).second)
|
|
continue;
|
|
|
|
// Prevent against checking against the same bound generic type
|
|
// over and over again. Doing so means redundant work in the best
|
|
// case. In the worst case, we'll produce lots of duplicate solutions
|
|
// for this constraint system, which is problematic for overload
|
|
// resolution.
|
|
if (type->hasTypeVariable()) {
|
|
auto triedBinding = false;
|
|
if (auto BGT = type->getAs<BoundGenericType>()) {
|
|
for (auto bt : boundTypes) {
|
|
if (auto BBGT = bt->getAs<BoundGenericType>()) {
|
|
if (BGT != BBGT &&
|
|
BGT->getDecl() == BBGT->getDecl()) {
|
|
triedBinding = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (triedBinding)
|
|
continue;
|
|
}
|
|
|
|
if (tc.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = cs.getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(depth * 2)
|
|
<< "(trying " << typeVar->getString() << " := " << type->getString()
|
|
<< "\n";
|
|
}
|
|
|
|
// Try to solve the system with typeVar := type
|
|
ConstraintSystem::SolverScope scope(cs);
|
|
if (binding.DefaultedProtocol) {
|
|
// If we were able to solve this without considering
|
|
// default literals, don't bother looking at default literals.
|
|
if (!sawFirstLiteralConstraint) {
|
|
sawFirstLiteralConstraint = true;
|
|
if (anySolved)
|
|
break;
|
|
}
|
|
type = cs.openBindingType(type, typeVar->getImpl().getLocator());
|
|
}
|
|
|
|
// FIXME: We want the locator that indicates where the binding came
|
|
// from.
|
|
cs.addConstraint(ConstraintKind::Bind,
|
|
typeVar,
|
|
type,
|
|
typeVar->getImpl().getLocator());
|
|
|
|
// If this was from a defaultable binding note that.
|
|
if (binding.isDefaultableBinding()) {
|
|
cs.DefaultedConstraints.push_back(binding.DefaultableBinding);
|
|
}
|
|
|
|
if (!cs.solveRec(solutions, allowFreeTypeVariables))
|
|
anySolved = true;
|
|
|
|
if (tc.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = cs.getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(depth * 2) << ")\n";
|
|
}
|
|
}
|
|
|
|
// If we found any solution, we're done.
|
|
if (anySolved)
|
|
break;
|
|
|
|
// None of the children had solutions, enumerate supertypes and
|
|
// try again.
|
|
SmallVector<PotentialBinding, 4> newBindings;
|
|
|
|
// Enumerate the supertypes of each of the types we tried.
|
|
for (auto binding : bindings) {
|
|
const auto type = binding.BindingType;
|
|
if (type->hasError())
|
|
continue;
|
|
|
|
// After our first pass, note that we've explored these
|
|
// types.
|
|
if (tryCount == 0)
|
|
exploredTypes.insert(type->getCanonicalType());
|
|
|
|
// If we have a protocol with a default type, look for alternative
|
|
// types to the default.
|
|
if (tryCount == 0 && binding.DefaultedProtocol) {
|
|
KnownProtocolKind knownKind
|
|
= *((*binding.DefaultedProtocol)->getKnownProtocolKind());
|
|
for (auto altType : cs.getAlternativeLiteralTypes(knownKind)) {
|
|
if (exploredTypes.insert(altType->getCanonicalType()).second)
|
|
newBindings.push_back({altType, AllowedBindingKind::Subtypes,
|
|
binding.DefaultedProtocol});
|
|
}
|
|
}
|
|
|
|
// Handle simple subtype bindings.
|
|
if (binding.Kind == AllowedBindingKind::Subtypes &&
|
|
typeVar->getImpl().canBindToLValue() &&
|
|
!type->isLValueType() &&
|
|
!type->is<InOutType>()) {
|
|
// Try lvalue qualification in addition to rvalue qualification.
|
|
auto subtype = LValueType::get(type);
|
|
if (exploredTypes.insert(subtype->getCanonicalType()).second)
|
|
newBindings.push_back({subtype, binding.Kind, None});
|
|
}
|
|
|
|
if (binding.Kind == AllowedBindingKind::Subtypes) {
|
|
if (auto tupleTy = type->getAs<TupleType>()) {
|
|
int scalarIdx = tupleTy->getElementForScalarInit();
|
|
if (scalarIdx >= 0) {
|
|
auto eltType = tupleTy->getElementType(scalarIdx);
|
|
if (exploredTypes.insert(eltType->getCanonicalType()).second)
|
|
newBindings.push_back({eltType, binding.Kind, None});
|
|
}
|
|
}
|
|
|
|
// If we were unsuccessful solving for T?, try solving for T.
|
|
if (auto objTy = type->getOptionalObjectType()) {
|
|
if (exploredTypes.insert(objTy->getCanonicalType()).second) {
|
|
// If T is a type variable, only attempt this if both the
|
|
// type variable we are trying bindings for, and the type
|
|
// variable we will attempt to bind, both have the same
|
|
// polarity with respect to being able to bind lvalues.
|
|
if (auto otherTypeVar = objTy->getAs<TypeVariableType>()) {
|
|
if (typeVar->getImpl().canBindToLValue() ==
|
|
otherTypeVar->getImpl().canBindToLValue()) {
|
|
newBindings.push_back({objTy, binding.Kind, None});
|
|
}
|
|
} else {
|
|
newBindings.push_back({objTy, binding.Kind, None});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (binding.Kind != AllowedBindingKind::Supertypes)
|
|
continue;
|
|
|
|
for (auto supertype : enumerateDirectSupertypes(cs.getTypeChecker(),
|
|
type)) {
|
|
// If we're not allowed to try this binding, skip it.
|
|
auto simpleSuper = checkTypeOfBinding(cs, typeVar, supertype);
|
|
if (!simpleSuper)
|
|
continue;
|
|
|
|
// If we haven't seen this supertype, add it.
|
|
if (exploredTypes.insert((*simpleSuper)->getCanonicalType()).second)
|
|
newBindings.push_back({*simpleSuper, binding.Kind, None});
|
|
}
|
|
}
|
|
|
|
// If we didn't compute any new bindings, we're done.
|
|
if (newBindings.empty())
|
|
break;
|
|
|
|
// We have a new set of bindings; use them for our next loop.
|
|
storedBindings = std::move(newBindings);
|
|
bindings = storedBindings;
|
|
}
|
|
|
|
return !anySolved;
|
|
}
|
|
|
|
/// \brief Solve the system of constraints.
|
|
///
|
|
/// \param allowFreeTypeVariables How to bind free type variables in
|
|
/// the solution.
|
|
///
|
|
/// \returns a solution if a single unambiguous one could be found, or None if
|
|
/// ambiguous or unsolvable.
|
|
Optional<Solution>
|
|
ConstraintSystem::solveSingle(FreeTypeVariableBinding allowFreeTypeVariables) {
|
|
SmallVector<Solution, 4> solutions;
|
|
if (solve(solutions, allowFreeTypeVariables) ||
|
|
solutions.size() != 1)
|
|
return Optional<Solution>();
|
|
|
|
return std::move(solutions[0]);
|
|
}
|
|
|
|
bool ConstraintSystem::Candidate::solve() {
|
|
// Don't attempt to solve candidate if there is closure
|
|
// expression involved, because it's handled specially
|
|
// by parent constraint system (e.g. parameter lists).
|
|
bool containsClosure = false;
|
|
E->forEachChildExpr([&](Expr *childExpr) -> Expr * {
|
|
if (isa<ClosureExpr>(childExpr)) {
|
|
containsClosure = true;
|
|
return nullptr;
|
|
}
|
|
return childExpr;
|
|
});
|
|
|
|
if (containsClosure)
|
|
return false;
|
|
|
|
auto cleanupImplicitExprs = [&](Expr *expr) {
|
|
expr->forEachChildExpr([&](Expr *childExpr) -> Expr * {
|
|
Type type = childExpr->getType();
|
|
if (childExpr->isImplicit() && type && type->hasTypeVariable())
|
|
childExpr->setType(Type());
|
|
return childExpr;
|
|
});
|
|
};
|
|
|
|
// Allocate new constraint system for sub-expression.
|
|
ConstraintSystem cs(TC, DC, None);
|
|
|
|
// Cleanup after constraint system generation/solving,
|
|
// because it would assign types to expressions, which
|
|
// might interfere with solving higher-level expressions.
|
|
ExprCleaner cleaner(E);
|
|
|
|
// Generate constraints for the new system.
|
|
if (auto generatedExpr = cs.generateConstraints(E)) {
|
|
E = generatedExpr;
|
|
} else {
|
|
// Failure to generate constraint system for sub-expression
|
|
// means we can't continue solving sub-expressions.
|
|
cleanupImplicitExprs(E);
|
|
return true;
|
|
}
|
|
|
|
// If there is contextual type present, add an explicit "conversion"
|
|
// constraint to the system.
|
|
if (!CT.isNull()) {
|
|
auto constraintKind = ConstraintKind::Conversion;
|
|
if (CTP == CTP_CallArgument)
|
|
constraintKind = ConstraintKind::ArgumentConversion;
|
|
|
|
cs.addConstraint(constraintKind, cs.getType(E), CT,
|
|
cs.getConstraintLocator(E), /*isFavored=*/true);
|
|
}
|
|
|
|
// Try to solve the system and record all available solutions.
|
|
llvm::SmallVector<Solution, 2> solutions;
|
|
{
|
|
SolverState state(cs);
|
|
|
|
// Use solveRec() instead of solve() in here, because solve()
|
|
// would try to deduce the best solution, which we don't
|
|
// really want. Instead, we want the reduced set of domain choices.
|
|
cs.solveRec(solutions, FreeTypeVariableBinding::Allow);
|
|
}
|
|
|
|
// Record found solutions as suggestions.
|
|
this->applySolutions(solutions);
|
|
|
|
// Let's double-check if we have any implicit expressions
|
|
// with type variables and nullify their types.
|
|
cleanupImplicitExprs(E);
|
|
|
|
// No solutions for the sub-expression means that either main expression
|
|
// needs salvaging or it's inconsistent (read: doesn't have solutions).
|
|
return solutions.empty();
|
|
}
|
|
|
|
void ConstraintSystem::Candidate::applySolutions(
|
|
llvm::SmallVectorImpl<Solution> &solutions) const {
|
|
// A collection of OSRs with their newly reduced domains,
|
|
// it's domains are sets because multiple solutions can have the same
|
|
// choice for one of the type variables, and we want no duplication.
|
|
llvm::SmallDenseMap<OverloadSetRefExpr *, llvm::SmallSet<ValueDecl *, 2>>
|
|
domains;
|
|
for (auto &solution : solutions) {
|
|
for (auto choice : solution.overloadChoices) {
|
|
// Some of the choices might not have locators.
|
|
if (!choice.getFirst())
|
|
continue;
|
|
|
|
auto anchor = choice.getFirst()->getAnchor();
|
|
// Anchor is not available or expression is not an overload set.
|
|
if (!anchor || !isa<OverloadSetRefExpr>(anchor))
|
|
continue;
|
|
|
|
auto OSR = cast<OverloadSetRefExpr>(anchor);
|
|
auto overload = choice.getSecond().choice;
|
|
auto type = overload.getDecl()->getInterfaceType();
|
|
|
|
// One of the solutions has polymorphic type assigned with one of it's
|
|
// type variables. Such functions can only be properly resolved
|
|
// via complete expression, so we'll have to forget solutions
|
|
// we have already recorded. They might not include all viable overload
|
|
// choices.
|
|
if (type->is<GenericFunctionType>()) {
|
|
return;
|
|
}
|
|
|
|
domains[OSR].insert(overload.getDecl());
|
|
}
|
|
}
|
|
|
|
// Reduce the domains.
|
|
for (auto &domain : domains) {
|
|
auto OSR = domain.getFirst();
|
|
auto &choices = domain.getSecond();
|
|
|
|
// If the domain wasn't reduced, skip it.
|
|
if (OSR->getDecls().size() == choices.size()) continue;
|
|
|
|
// Update the expression with the reduced domain.
|
|
MutableArrayRef<ValueDecl *> decls
|
|
= TC.Context.AllocateUninitialized<ValueDecl *>(choices.size());
|
|
std::uninitialized_copy(choices.begin(), choices.end(), decls.begin());
|
|
OSR->setDecls(decls);
|
|
}
|
|
}
|
|
|
|
void ConstraintSystem::shrink(Expr *expr) {
|
|
// Disable the shrink pass when constraint propagation is
|
|
// enabled. They achieve similar effects, and the shrink pass is
|
|
// known to have bad behavior in some cases.
|
|
if (TC.Context.LangOpts.EnableConstraintPropagation)
|
|
return;
|
|
|
|
typedef llvm::SmallDenseMap<Expr *, ArrayRef<ValueDecl *>> DomainMap;
|
|
|
|
// A collection of original domains of all of the expressions,
|
|
// so they can be restored in case of failure.
|
|
DomainMap domains;
|
|
|
|
struct ExprCollector : public ASTWalker {
|
|
Expr *PrimaryExpr;
|
|
|
|
// The primary constraint system.
|
|
ConstraintSystem &CS;
|
|
|
|
// All of the sub-expressions which are suitable to be solved
|
|
// separately from the main system e.g. binary expressions, collections,
|
|
// function calls, coercions etc.
|
|
llvm::SmallVector<Candidate, 4> Candidates;
|
|
|
|
// Counts the number of overload sets present in the tree so far.
|
|
// Note that the traversal is depth-first.
|
|
llvm::SmallVector<std::pair<ApplyExpr *, unsigned>, 4> ApplyExprs;
|
|
|
|
// A collection of original domains of all of the expressions,
|
|
// so they can be restored in case of failure.
|
|
DomainMap &Domains;
|
|
|
|
ExprCollector(Expr *expr, ConstraintSystem &cs, DomainMap &domains)
|
|
: PrimaryExpr(expr), CS(cs), Domains(domains) {}
|
|
|
|
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
|
|
// A dictionary expression is just a set of tuples; try to solve ones
|
|
// that have overload sets.
|
|
if (auto collectionExpr = dyn_cast<CollectionExpr>(expr)) {
|
|
visitCollectionExpr(collectionExpr, CS.getContextualType(expr),
|
|
CS.getContextualTypePurpose());
|
|
// Don't try to walk into the dictionary.
|
|
return {false, expr};
|
|
}
|
|
|
|
// Let's not attempt to type-check closures or expressions
|
|
// which constrain closures, because they require special handling
|
|
// when dealing with context and parameters declarations.
|
|
if (isa<ClosureExpr>(expr)) {
|
|
return {false, expr};
|
|
}
|
|
|
|
if (auto coerceExpr = dyn_cast<CoerceExpr>(expr)) {
|
|
visitCoerceExpr(coerceExpr);
|
|
return {false, expr};
|
|
}
|
|
|
|
if (auto OSR = dyn_cast<OverloadSetRefExpr>(expr)) {
|
|
Domains[OSR] = OSR->getDecls();
|
|
}
|
|
|
|
if (auto applyExpr = dyn_cast<ApplyExpr>(expr)) {
|
|
auto func = applyExpr->getFn();
|
|
// Let's record this function application for post-processing
|
|
// as well as if it contains overload set, see walkToExprPost.
|
|
ApplyExprs.push_back({applyExpr, isa<OverloadSetRefExpr>(func)});
|
|
}
|
|
|
|
return { true, expr };
|
|
}
|
|
|
|
/// Determine whether this is an arithmetic expression comprised entirely
|
|
/// of literals.
|
|
static bool isArithmeticExprOfLiterals(Expr *expr) {
|
|
expr = expr->getSemanticsProvidingExpr();
|
|
|
|
if (auto prefix = dyn_cast<PrefixUnaryExpr>(expr))
|
|
return isArithmeticExprOfLiterals(prefix->getArg());
|
|
|
|
if (auto postfix = dyn_cast<PostfixUnaryExpr>(expr))
|
|
return isArithmeticExprOfLiterals(postfix->getArg());
|
|
|
|
if (auto binary = dyn_cast<BinaryExpr>(expr))
|
|
return isArithmeticExprOfLiterals(binary->getArg()->getElement(0)) &&
|
|
isArithmeticExprOfLiterals(binary->getArg()->getElement(1));
|
|
|
|
return isa<IntegerLiteralExpr>(expr) || isa<FloatLiteralExpr>(expr);
|
|
}
|
|
|
|
Expr *walkToExprPost(Expr *expr) override {
|
|
if (expr == PrimaryExpr) {
|
|
// If this is primary expression and there are no candidates
|
|
// to be solved, let's not record it, because it's going to be
|
|
// solved regardless.
|
|
if (Candidates.empty())
|
|
return expr;
|
|
|
|
auto contextualType = CS.getContextualType();
|
|
// If there is a contextual type set for this expression.
|
|
if (!contextualType.isNull()) {
|
|
Candidates.push_back(Candidate(CS, expr, contextualType,
|
|
CS.getContextualTypePurpose()));
|
|
return expr;
|
|
}
|
|
|
|
// Or it's a function application with other candidates present.
|
|
if (isa<ApplyExpr>(expr)) {
|
|
Candidates.push_back(Candidate(CS, expr));
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
if (!isa<ApplyExpr>(expr))
|
|
return expr;
|
|
|
|
unsigned numOverloadSets = 0;
|
|
// Let's count how many overload sets do we have.
|
|
while (!ApplyExprs.empty()) {
|
|
auto &application = ApplyExprs.back();
|
|
auto applyExpr = application.first;
|
|
|
|
// Add overload sets tracked by current expression.
|
|
numOverloadSets += application.second;
|
|
ApplyExprs.pop_back();
|
|
|
|
// We've found the current expression, so record the number of
|
|
// overloads.
|
|
if (expr == applyExpr) {
|
|
ApplyExprs.push_back({applyExpr, numOverloadSets});
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If there are fewer than two overloads in the chain
|
|
// there is no point of solving this expression,
|
|
// because we won't be able to reduce its domain.
|
|
if (numOverloadSets > 1 && !isArithmeticExprOfLiterals(expr))
|
|
Candidates.push_back(Candidate(CS, expr));
|
|
|
|
return expr;
|
|
}
|
|
|
|
private:
|
|
/// \brief Extract type of the element from given collection type.
|
|
///
|
|
/// \param collection The type of the collection container.
|
|
///
|
|
/// \returns ErrorType on failure, properly constructed type otherwise.
|
|
Type extractElementType(Type collection) {
|
|
auto &ctx = CS.getASTContext();
|
|
if (collection.isNull() || collection->hasError())
|
|
return ErrorType::get(ctx);
|
|
|
|
auto base = collection.getPointer();
|
|
auto isInvalidType = [](Type type) -> bool {
|
|
return type.isNull() || type->hasUnresolvedType() ||
|
|
type->hasError();
|
|
};
|
|
|
|
// Array type.
|
|
if (auto array = dyn_cast<ArraySliceType>(base)) {
|
|
auto elementType = array->getBaseType();
|
|
// If base type is invalid let's return error type.
|
|
return isInvalidType(elementType) ? ErrorType::get(ctx) : elementType;
|
|
}
|
|
|
|
// Map or Set or any other associated collection type.
|
|
if (auto boundGeneric = dyn_cast<BoundGenericType>(base)) {
|
|
if (boundGeneric->hasUnresolvedType())
|
|
return ErrorType::get(ctx);
|
|
|
|
llvm::SmallVector<TupleTypeElt, 2> params;
|
|
for (auto &type : boundGeneric->getGenericArgs()) {
|
|
// One of the generic arguments in invalid or unresolved.
|
|
if (isInvalidType(type))
|
|
return ErrorType::get(ctx);
|
|
|
|
params.push_back(type);
|
|
}
|
|
|
|
// If there is just one parameter, let's return it directly.
|
|
if (params.size() == 1)
|
|
return params[0].getType();
|
|
|
|
return TupleType::get(params, ctx);
|
|
}
|
|
|
|
return ErrorType::get(ctx);
|
|
}
|
|
|
|
bool isSuitableCollection(TypeRepr *collectionTypeRepr) {
|
|
// Only generic identifier, array or dictionary.
|
|
switch (collectionTypeRepr->getKind()) {
|
|
case TypeReprKind::GenericIdent:
|
|
case TypeReprKind::Array:
|
|
case TypeReprKind::Dictionary:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void visitCoerceExpr(CoerceExpr *coerceExpr) {
|
|
auto subExpr = coerceExpr->getSubExpr();
|
|
// Coerce expression is valid only if it has sub-expression.
|
|
if (!subExpr) return;
|
|
|
|
unsigned numOverloadSets = 0;
|
|
subExpr->forEachChildExpr([&](Expr *childExpr) -> Expr * {
|
|
if (isa<OverloadSetRefExpr>(childExpr)) {
|
|
++numOverloadSets;
|
|
return childExpr;
|
|
}
|
|
|
|
if (auto nestedCoerceExpr = dyn_cast<CoerceExpr>(childExpr)) {
|
|
visitCoerceExpr(nestedCoerceExpr);
|
|
// Don't walk inside of nested coercion expression directly,
|
|
// that is be done by recursive call to visitCoerceExpr.
|
|
return nullptr;
|
|
}
|
|
|
|
// If sub-expression we are trying to coerce to type is a collection,
|
|
// let's allow collector discover it with assigned contextual type
|
|
// of coercion, which allows collections to be solved in parts.
|
|
if (auto collectionExpr = dyn_cast<CollectionExpr>(childExpr)) {
|
|
auto castTypeLoc = coerceExpr->getCastTypeLoc();
|
|
auto typeRepr = castTypeLoc.getTypeRepr();
|
|
|
|
if (typeRepr && isSuitableCollection(typeRepr)) {
|
|
// Clone representative to avoid modifying in-place,
|
|
// FIXME: We should try and silently resolve the type here,
|
|
// instead of cloning representative.
|
|
auto coercionRepr = typeRepr->clone(CS.getASTContext());
|
|
// Let's try to resolve coercion type from cloned representative.
|
|
auto coercionType = CS.TC.resolveType(coercionRepr, CS.DC,
|
|
TypeResolutionOptions());
|
|
|
|
// Looks like coercion type is invalid, let's skip this sub-tree.
|
|
if (coercionType->hasError())
|
|
return nullptr;
|
|
|
|
// Visit collection expression inline.
|
|
visitCollectionExpr(collectionExpr, coercionType,
|
|
CTP_CoerceOperand);
|
|
}
|
|
}
|
|
|
|
return childExpr;
|
|
});
|
|
|
|
// It's going to be inefficient to try and solve
|
|
// coercion in parts, so let's just make it a candidate directly,
|
|
// if it contains at least a single overload set.
|
|
|
|
if (numOverloadSets > 0)
|
|
Candidates.push_back(Candidate(CS, coerceExpr));
|
|
}
|
|
|
|
void visitCollectionExpr(CollectionExpr *collectionExpr,
|
|
Type contextualType = Type(),
|
|
ContextualTypePurpose CTP = CTP_Unused) {
|
|
// If there is a contextual type set for this collection,
|
|
// let's propagate it to the candidate.
|
|
if (!contextualType.isNull()) {
|
|
auto elementType = extractElementType(contextualType);
|
|
// If we couldn't deduce element type for the collection, let's
|
|
// not attempt to solve it.
|
|
if (elementType->hasError())
|
|
return;
|
|
|
|
contextualType = elementType;
|
|
}
|
|
|
|
for (auto element : collectionExpr->getElements()) {
|
|
unsigned numOverloads = 0;
|
|
element->walk(OverloadSetCounter(numOverloads));
|
|
|
|
// There are no overload sets in the element; skip it.
|
|
if (numOverloads == 0)
|
|
continue;
|
|
|
|
// Record each of the collection elements, which passed
|
|
// number of overload sets rule, as a candidate for solving
|
|
// with contextual type of the collection.
|
|
Candidates.push_back(Candidate(CS, element, contextualType, CTP));
|
|
}
|
|
}
|
|
};
|
|
|
|
ExprCollector collector(expr, *this, domains);
|
|
|
|
// Collect all of the binary/unary and call sub-expressions
|
|
// so we can start solving them separately.
|
|
expr->walk(collector);
|
|
|
|
for (auto &candidate : collector.Candidates) {
|
|
// If there are no results, let's forget everything we know about the
|
|
// system so far. This actually is ok, because some of the expressions
|
|
// might require manual salvaging.
|
|
if (candidate.solve()) {
|
|
// Let's restore all of the original OSR domains for this sub-expression,
|
|
// this means that we can still make forward progress with solving of the
|
|
// top sub-expressions.
|
|
candidate.getExpr()->forEachChildExpr([&](Expr *childExpr) -> Expr * {
|
|
if (auto OSR = dyn_cast<OverloadSetRefExpr>(childExpr)) {
|
|
auto domain = domains.find(OSR);
|
|
if (domain == domains.end())
|
|
return childExpr;
|
|
|
|
OSR->setDecls(domain->getSecond());
|
|
}
|
|
|
|
return childExpr;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::solve(Expr *&expr,
|
|
Type convertType,
|
|
ExprTypeCheckListener *listener,
|
|
SmallVectorImpl<Solution> &solutions,
|
|
FreeTypeVariableBinding allowFreeTypeVariables) {
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log << "---Constraint solving for the expression at ";
|
|
auto R = expr->getSourceRange();
|
|
if (R.isValid()) {
|
|
R.print(log, TC.Context.SourceMgr, /*PrintText=*/ false);
|
|
} else {
|
|
log << "<invalid range>";
|
|
}
|
|
log << "---\n";
|
|
}
|
|
|
|
assert(!solverState && "use solveRec for recursive calls");
|
|
|
|
// Try to shrink the system by reducing disjunction domains. This
|
|
// goes through every sub-expression and generate its own sub-system, to
|
|
// try to reduce the domains of those subexpressions.
|
|
shrink(expr);
|
|
|
|
// Generate constraints for the main system.
|
|
if (auto generatedExpr = generateConstraints(expr))
|
|
expr = generatedExpr;
|
|
else {
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// If there is a type that we're expected to convert to, add the conversion
|
|
// constraint.
|
|
if (convertType) {
|
|
auto constraintKind = ConstraintKind::Conversion;
|
|
if (getContextualTypePurpose() == CTP_CallArgument)
|
|
constraintKind = ConstraintKind::ArgumentConversion;
|
|
|
|
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
|
|
convertType = convertType.transform([&](Type type) -> Type {
|
|
if (type->is<UnresolvedType>())
|
|
return createTypeVariable(getConstraintLocator(expr), 0);
|
|
return type;
|
|
});
|
|
}
|
|
|
|
addConstraint(constraintKind, getType(expr), convertType,
|
|
getConstraintLocator(expr), /*isFavored*/ true);
|
|
}
|
|
|
|
// Notify the listener that we've built the constraint system.
|
|
if (listener && listener->builtConstraints(*this, expr)) {
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log << "---Initial constraints for the given expression---\n";
|
|
expr->print(log);
|
|
log << "\n";
|
|
print(log);
|
|
}
|
|
|
|
// Try to solve the constraint system using computed suggestions.
|
|
solve(solutions, allowFreeTypeVariables);
|
|
|
|
// If there are no solutions let's mark system as unsolved,
|
|
// and solved otherwise even if there are multiple solutions still present.
|
|
|
|
// There was a Swift 3 bug that allowed us to return Solved if we
|
|
// had found at least one solution before deciding an expression was
|
|
// "too complex". Maintain that behavior, but for Swift > 3 return
|
|
// Unsolved in these cases.
|
|
auto tooComplex = getExpressionTooComplex(solutions) &&
|
|
!getASTContext().isSwiftVersion3();
|
|
auto unsolved = tooComplex || solutions.empty();
|
|
|
|
return unsolved ? SolutionKind::Unsolved : SolutionKind::Solved;
|
|
}
|
|
|
|
bool ConstraintSystem::solve(SmallVectorImpl<Solution> &solutions,
|
|
FreeTypeVariableBinding allowFreeTypeVariables) {
|
|
// Set up solver state.
|
|
SolverState state(*this);
|
|
|
|
// Simplify any constraints left active after constraint generation
|
|
// and optimization. Return if the resulting system has no
|
|
// solutions.
|
|
if (failedConstraint || simplify())
|
|
return true;
|
|
|
|
// If the experimental constraint propagation pass is enabled, run it.
|
|
if (TC.Context.LangOpts.EnableConstraintPropagation)
|
|
if (propagateConstraints())
|
|
return true;
|
|
|
|
// Solve the system.
|
|
solveRec(solutions, allowFreeTypeVariables);
|
|
|
|
// If there is more than one viable system, attempt to pick the best
|
|
// solution.
|
|
auto size = solutions.size();
|
|
if (size > 1) {
|
|
if (auto best = findBestSolution(solutions, /*minimize=*/false)) {
|
|
if (*best != 0)
|
|
solutions[0] = std::move(solutions[*best]);
|
|
solutions.erase(solutions.begin() + 1, solutions.end());
|
|
}
|
|
}
|
|
|
|
// We fail if there is no solution.
|
|
return solutions.empty();
|
|
}
|
|
|
|
bool ConstraintSystem::solveRec(SmallVectorImpl<Solution> &solutions,
|
|
FreeTypeVariableBinding allowFreeTypeVariables){
|
|
// If we already failed, or simplification fails, we're done.
|
|
if (failedConstraint || simplify()) {
|
|
return true;
|
|
} else {
|
|
assert(ActiveConstraints.empty() && "Active constraints remain?");
|
|
}
|
|
|
|
// If there are no constraints remaining, we're done. Save this solution.
|
|
if (InactiveConstraints.empty()) {
|
|
// If this solution is worse than the best solution we've seen so far,
|
|
// skip it.
|
|
if (worseThanBestSolution())
|
|
return true;
|
|
|
|
// If any free type variables remain and we're not allowed to have them,
|
|
// fail.
|
|
if (allowFreeTypeVariables == FreeTypeVariableBinding::Disallow &&
|
|
hasFreeTypeVariables())
|
|
return true;
|
|
|
|
auto solution = finalize(allowFreeTypeVariables);
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth * 2)
|
|
<< "(found solution " << CurrentScore << ")\n";
|
|
}
|
|
|
|
solutions.push_back(std::move(solution));
|
|
return false;
|
|
}
|
|
|
|
// Contract the edges of the constraint graph.
|
|
CG.optimize();
|
|
|
|
// Compute the connected components of the constraint graph.
|
|
// FIXME: We're seeding typeVars with TypeVariables so that the
|
|
// connected-components algorithm only considers those type variables within
|
|
// our component. There are clearly better ways to do this.
|
|
SmallVector<TypeVariableType *, 16> typeVars(TypeVariables);
|
|
SmallVector<unsigned, 16> components;
|
|
unsigned numComponents = CG.computeConnectedComponents(typeVars, components);
|
|
|
|
// If we don't have more than one component, just solve the whole
|
|
// system.
|
|
if (numComponents < 2) {
|
|
return solveSimplified(solutions, allowFreeTypeVariables);
|
|
}
|
|
|
|
if (TC.Context.LangOpts.DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
|
|
// Verify that the constraint graph is valid.
|
|
CG.verify();
|
|
|
|
log << "---Constraint graph---\n";
|
|
CG.print(log);
|
|
|
|
log << "---Connected components---\n";
|
|
CG.printConnectedComponents(log);
|
|
}
|
|
|
|
// Construct a mapping from type variables and constraints to their
|
|
// owning component.
|
|
llvm::DenseMap<TypeVariableType *, unsigned> typeVarComponent;
|
|
llvm::DenseMap<Constraint *, unsigned> constraintComponent;
|
|
for (unsigned i = 0, n = typeVars.size(); i != n; ++i) {
|
|
// Record the component of this type variable.
|
|
typeVarComponent[typeVars[i]] = components[i];
|
|
|
|
// Record the component of each of the constraints.
|
|
for (auto constraint : CG[typeVars[i]].getConstraints())
|
|
constraintComponent[constraint] = components[i];
|
|
}
|
|
|
|
// Add the orphaned components to the mapping from constraints to components.
|
|
unsigned firstOrphanedConstraint =
|
|
numComponents - CG.getOrphanedConstraints().size();
|
|
{
|
|
unsigned component = firstOrphanedConstraint;
|
|
for (auto constraint : CG.getOrphanedConstraints())
|
|
constraintComponent[constraint] = component++;
|
|
}
|
|
|
|
// Sort the constraints into buckets based on component number.
|
|
std::unique_ptr<ConstraintList[]> constraintBuckets(
|
|
new ConstraintList[numComponents]);
|
|
while (!InactiveConstraints.empty()) {
|
|
auto *constraint = &InactiveConstraints.front();
|
|
InactiveConstraints.pop_front();
|
|
constraintBuckets[constraintComponent[constraint]].push_back(constraint);
|
|
}
|
|
|
|
// Remove all of the orphaned constraints; we'll introduce them as needed.
|
|
auto allOrphanedConstraints = CG.takeOrphanedConstraints();
|
|
|
|
// Function object that returns all constraints placed into buckets
|
|
// back to the list of constraints.
|
|
auto returnAllConstraints = [&] {
|
|
assert(InactiveConstraints.empty() && "Already have constraints?");
|
|
for (unsigned component = 0; component != numComponents; ++component) {
|
|
InactiveConstraints.splice(InactiveConstraints.end(),
|
|
constraintBuckets[component]);
|
|
}
|
|
CG.setOrphanedConstraints(std::move(allOrphanedConstraints));
|
|
};
|
|
|
|
// Compute the partial solutions produced for each connected component.
|
|
std::unique_ptr<SmallVector<Solution, 4>[]>
|
|
partialSolutions(new SmallVector<Solution, 4>[numComponents]);
|
|
Optional<Score> PreviousBestScore = solverState->BestScore;
|
|
for (unsigned component = 0; component != numComponents; ++component) {
|
|
assert(InactiveConstraints.empty() &&
|
|
"Some constraints were not transferred?");
|
|
++solverState->NumComponentsSplit;
|
|
|
|
// Collect the constraints for this component.
|
|
InactiveConstraints.splice(InactiveConstraints.end(),
|
|
constraintBuckets[component]);
|
|
|
|
llvm::SmallVector<TypeVariableType *, 16> allTypeVariables
|
|
= std::move(TypeVariables);
|
|
|
|
Constraint *orphaned = nullptr;
|
|
if (component < firstOrphanedConstraint) {
|
|
// Collect the type variables that are not part of a different
|
|
// component; this includes type variables that are part of the
|
|
// component as well as already-resolved type variables.
|
|
for (auto typeVar : allTypeVariables) {
|
|
auto known = typeVarComponent.find(typeVar);
|
|
if (known != typeVarComponent.end() && known->second != component)
|
|
continue;
|
|
|
|
TypeVariables.push_back(typeVar);
|
|
}
|
|
} else {
|
|
// Get the orphaned constraint.
|
|
orphaned = allOrphanedConstraints[component - firstOrphanedConstraint];
|
|
}
|
|
CG.setOrphanedConstraint(orphaned);
|
|
|
|
// Solve for this component. If it fails, we're done.
|
|
bool failed;
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth * 2) << "(solving component #"
|
|
<< component << "\n";
|
|
}
|
|
{
|
|
// Introduce a scope for this partial solution.
|
|
SolverScope scope(*this);
|
|
llvm::SaveAndRestore<SolverScope *>
|
|
partialSolutionScope(solverState->PartialSolutionScope, &scope);
|
|
|
|
failed = solveSimplified(partialSolutions[component],
|
|
allowFreeTypeVariables);
|
|
}
|
|
|
|
// Put the constraints back into their original bucket.
|
|
auto &bucket = constraintBuckets[component];
|
|
bucket.splice(bucket.end(), InactiveConstraints);
|
|
|
|
if (failed) {
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth * 2) << "failed component #"
|
|
<< component << ")\n";
|
|
}
|
|
|
|
TypeVariables = std::move(allTypeVariables);
|
|
returnAllConstraints();
|
|
return true;
|
|
}
|
|
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth * 2) << "finished component #"
|
|
<< component << ")\n";
|
|
}
|
|
|
|
assert(!partialSolutions[component].empty() &&" No solutions?");
|
|
|
|
// Move the type variables back, clear out constraints; we're
|
|
// ready for the next component.
|
|
TypeVariables = std::move(allTypeVariables);
|
|
|
|
// For each of the partial solutions, subtract off the current score.
|
|
// It doesn't contribute.
|
|
for (auto &solution : partialSolutions[component])
|
|
solution.getFixedScore() -= CurrentScore;
|
|
|
|
// Restore the previous best score.
|
|
solverState->BestScore = PreviousBestScore;
|
|
}
|
|
|
|
// Move the constraints back. The system is back in a normal state.
|
|
returnAllConstraints();
|
|
|
|
// When there are multiple partial solutions for a given connected component,
|
|
// rank those solutions to pick the best ones. This limits the number of
|
|
// combinations we need to produce; in the common case, down to a single
|
|
// combination.
|
|
for (unsigned component = 0; component != numComponents; ++component) {
|
|
auto &solutions = partialSolutions[component];
|
|
// If there's a single best solution, keep only that one.
|
|
// Otherwise, the set of solutions will at least have been minimized.
|
|
if (auto best = findBestSolution(solutions, /*minimize=*/true)) {
|
|
if (*best > 0)
|
|
solutions[0] = std::move(solutions[*best]);
|
|
solutions.erase(solutions.begin() + 1, solutions.end());
|
|
}
|
|
}
|
|
|
|
// Produce all combinations of partial solutions.
|
|
SmallVector<unsigned, 2> indices(numComponents, 0);
|
|
bool done = false;
|
|
bool anySolutions = false;
|
|
do {
|
|
// Create a new solver scope in which we apply all of the partial
|
|
// solutions.
|
|
SolverScope scope(*this);
|
|
for (unsigned i = 0; i != numComponents; ++i)
|
|
applySolution(partialSolutions[i][indices[i]]);
|
|
|
|
// This solution might be worse than the best solution found so far. If so,
|
|
// skip it.
|
|
if (!worseThanBestSolution()) {
|
|
// Finalize this solution.
|
|
auto solution = finalize(allowFreeTypeVariables);
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth * 2)
|
|
<< "(composed solution " << CurrentScore << ")\n";
|
|
}
|
|
|
|
// Save this solution.
|
|
solutions.push_back(std::move(solution));
|
|
|
|
anySolutions = true;
|
|
}
|
|
|
|
// Find the next combination.
|
|
for (unsigned n = numComponents; n > 0; --n) {
|
|
++indices[n-1];
|
|
|
|
// If we haven't run out of solutions yet, we're done.
|
|
if (indices[n-1] < partialSolutions[n-1].size())
|
|
break;
|
|
|
|
// If we ran out of solutions at the first position, we're done.
|
|
if (n == 1) {
|
|
done = true;
|
|
break;
|
|
}
|
|
|
|
// Zero out the indices from here to the end.
|
|
for (unsigned i = n-1; i != numComponents; ++i)
|
|
indices[i] = 0;
|
|
}
|
|
} while (!done);
|
|
|
|
return !anySolutions;
|
|
}
|
|
|
|
/// Whether we should short-circuit a disjunction that already has a
|
|
/// solution when we encounter the given constraint.
|
|
static bool shortCircuitDisjunctionAt(Constraint *constraint,
|
|
Constraint *successfulConstraint) {
|
|
|
|
// If the successfully applied constraint is favored, we'll consider that to
|
|
// be the "best".
|
|
if (successfulConstraint->isFavored() && !constraint->isFavored()) {
|
|
return true;
|
|
}
|
|
|
|
// Anything without a fix is better than anything with a fix.
|
|
if (constraint->getFix() && !successfulConstraint->getFix())
|
|
return true;
|
|
|
|
if (auto restriction = constraint->getRestriction()) {
|
|
// Non-optional conversions are better than optional-to-optional
|
|
// conversions.
|
|
if (*restriction == ConversionRestrictionKind::OptionalToOptional)
|
|
return true;
|
|
|
|
// Array-to-pointer conversions are better than inout-to-pointer conversions.
|
|
if (auto successfulRestriction = successfulConstraint->getRestriction()) {
|
|
if (*successfulRestriction == ConversionRestrictionKind::ArrayToPointer
|
|
&& *restriction == ConversionRestrictionKind::InoutToPointer)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Implicit conversions are better than checked casts.
|
|
if (constraint->getKind() == ConstraintKind::CheckedCast)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void ConstraintSystem::collectDisjunctions(
|
|
SmallVectorImpl<Constraint *> &disjunctions) {
|
|
for (auto &constraint : InactiveConstraints) {
|
|
if (constraint.getKind() == ConstraintKind::Disjunction)
|
|
disjunctions.push_back(&constraint);
|
|
}
|
|
}
|
|
|
|
static std::pair<PotentialBindings, TypeVariableType *>
|
|
determineBestBindings(ConstraintSystem &CS) {
|
|
// Look for potential type variable bindings.
|
|
TypeVariableType *bestTypeVar = nullptr;
|
|
PotentialBindings bestBindings;
|
|
for (auto typeVar : CS.getTypeVariables()) {
|
|
// Skip any type variables that are bound.
|
|
if (typeVar->getImpl().hasRepresentativeOrFixed())
|
|
continue;
|
|
|
|
// Get potential bindings.
|
|
auto bindings = getPotentialBindings(CS, typeVar);
|
|
if (!bindings)
|
|
continue;
|
|
|
|
if (CS.TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = CS.getASTContext().TypeCheckerDebug->getStream();
|
|
bindings.dump(typeVar, log, CS.solverState->depth * 2);
|
|
}
|
|
|
|
// If these are the first bindings, or they are better than what
|
|
// we saw before, use them instead.
|
|
if (!bestTypeVar || bindings < bestBindings) {
|
|
bestBindings = std::move(bindings);
|
|
bestTypeVar = typeVar;
|
|
}
|
|
}
|
|
|
|
return std::make_pair(bestBindings, bestTypeVar);
|
|
}
|
|
|
|
Constraint *getApplicableFunctionConstraint(ConstraintSystem &CS,
|
|
Constraint *disjunction) {
|
|
auto *nested = disjunction->getNestedConstraints().front();
|
|
auto *firstTy = nested->getFirstType()->castTo<TypeVariableType>();
|
|
|
|
// FIXME: Is there something else we should be doing when a member
|
|
// of a disjunction is already bound because it's in an
|
|
// equivalence class?
|
|
if (CS.getFixedType(firstTy))
|
|
return nullptr;
|
|
|
|
Constraint *found = nullptr;
|
|
for (auto *constraint : CS.getConstraintGraph()[firstTy].getConstraints()) {
|
|
if (constraint->getKind() != ConstraintKind::ApplicableFunction)
|
|
continue;
|
|
|
|
// Unapplied function reference, e.g. a.map(String.init)
|
|
if (!constraint->getSecondType()->isEqual(firstTy))
|
|
return nullptr;
|
|
|
|
found = constraint;
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
return nullptr;
|
|
|
|
#if !defined(NDEBUG)
|
|
for (auto *constraint : CS.getConstraintGraph()[firstTy].getConstraints()) {
|
|
if (constraint == found)
|
|
continue;
|
|
|
|
assert(constraint->getKind() != ConstraintKind::ApplicableFunction &&
|
|
"Type variable is involved in more than one applicable!");
|
|
}
|
|
#endif
|
|
|
|
return found;
|
|
}
|
|
|
|
static unsigned countUnboundTypes(ConstraintSystem &CS,
|
|
Constraint *disjunction) {
|
|
auto *nested = disjunction->getNestedConstraints().front();
|
|
assert(nested->getKind() == ConstraintKind::BindOverload &&
|
|
"Expected bind overload constraint!");
|
|
|
|
auto firstTy = nested->getFirstType();
|
|
|
|
if (!firstTy->is<FunctionType>()) {
|
|
// If the disjunction is of the form "$T1 bound to..." we should have
|
|
// an associated applicable function constraint.
|
|
auto *applicableFn = getApplicableFunctionConstraint(CS, disjunction);
|
|
|
|
// If we have an unapplied function, we should be able to leave
|
|
// that disjunction for last without any downside.
|
|
if (!applicableFn)
|
|
return 100;
|
|
|
|
firstTy = applicableFn->getFirstType();
|
|
}
|
|
|
|
auto *fnTy = firstTy->getAs<FunctionType>();
|
|
assert(fnTy && "Expected function type!");
|
|
auto simplifiedTy = CS.simplifyType(fnTy->getInput());
|
|
|
|
SmallPtrSet<TypeVariableType *, 4> typeVars;
|
|
findInferableTypeVars(simplifiedTy, typeVars);
|
|
unsigned count = 0;
|
|
for (auto *tv : typeVars)
|
|
if (!CS.getFixedType(tv))
|
|
++count;
|
|
|
|
return count;
|
|
}
|
|
|
|
// Is this constraint a bind overload to a generic operator or one
|
|
// that is unavailable?
|
|
static bool isGenericOperatorOrUnavailable(Constraint *constraint) {
|
|
if (constraint->getKind() != ConstraintKind::BindOverload ||
|
|
constraint->getOverloadChoice().getKind() != OverloadChoiceKind::Decl ||
|
|
!constraint->getOverloadChoice().getDecl()->isOperator())
|
|
return false;
|
|
|
|
auto decl = constraint->getOverloadChoice().getDecl();
|
|
auto &ctx = decl->getASTContext();
|
|
|
|
return decl->getInterfaceType()->is<GenericFunctionType>() ||
|
|
decl->getAttrs().isUnavailable(ctx);
|
|
}
|
|
|
|
/// Whether this constraint refers to a symmetric operator.
|
|
static bool isSymmetricOperator(Constraint *constraint) {
|
|
if (constraint->getKind() != ConstraintKind::BindOverload ||
|
|
constraint->getOverloadChoice().getKind() != OverloadChoiceKind::Decl ||
|
|
!constraint->getOverloadChoice().getDecl()->isOperator())
|
|
return false;
|
|
|
|
// If it's a binary operator, check that the types on both sides are the
|
|
// same. Otherwise, don't perform this optimization.
|
|
auto func = dyn_cast<FuncDecl>(constraint->getOverloadChoice().getDecl());
|
|
auto paramList =
|
|
func->getParameterList(func->getDeclContext()->isTypeContext());
|
|
if (paramList->size() != 2)
|
|
return true;
|
|
|
|
auto firstType =
|
|
paramList->get(0)->getInterfaceType()->getLValueOrInOutObjectType();
|
|
auto secondType =
|
|
paramList->get(1)->getInterfaceType()->getLValueOrInOutObjectType();
|
|
return firstType->isEqual(secondType);
|
|
}
|
|
|
|
bool ConstraintSystem::solveSimplified(
|
|
SmallVectorImpl<Solution> &solutions,
|
|
FreeTypeVariableBinding allowFreeTypeVariables) {
|
|
|
|
SmallVector<Constraint *, 4> disjunctions;
|
|
collectDisjunctions(disjunctions);
|
|
|
|
TypeVariableType *bestTypeVar = nullptr;
|
|
PotentialBindings bestBindings;
|
|
std::tie(bestBindings, bestTypeVar) = determineBestBindings(*this);
|
|
|
|
// If we have a binding that does not involve type variables, or we have
|
|
// no other option, go ahead and try the bindings for this type variable.
|
|
if (bestBindings &&
|
|
(disjunctions.empty() ||
|
|
(!bestBindings.InvolvesTypeVariables && !bestBindings.FullyBound &&
|
|
bestBindings.LiteralBinding == LiteralBindingKind::None))) {
|
|
return tryTypeVariableBindings(*this, solverState->depth, bestTypeVar,
|
|
bestBindings.Bindings, solutions,
|
|
allowFreeTypeVariables);
|
|
}
|
|
|
|
// If there are no disjunctions we can't solve this system unless we have
|
|
// free type variables and are allowing them in the solution.
|
|
if (disjunctions.empty()) {
|
|
if (allowFreeTypeVariables == FreeTypeVariableBinding::Disallow ||
|
|
!hasFreeTypeVariables())
|
|
return true;
|
|
|
|
// If this solution is worse than the best solution we've seen so far,
|
|
// skip it.
|
|
if (worseThanBestSolution())
|
|
return true;
|
|
|
|
// If we only have relational or member constraints and are allowing
|
|
// free type variables, save the solution.
|
|
for (auto &constraint : InactiveConstraints) {
|
|
switch (constraint.getClassification()) {
|
|
case ConstraintClassification::Relational:
|
|
case ConstraintClassification::Member:
|
|
continue;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
auto solution = finalize(allowFreeTypeVariables);
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth * 2) << "(found solution)\n";
|
|
}
|
|
|
|
solutions.push_back(std::move(solution));
|
|
return false;
|
|
}
|
|
|
|
// Select a disjunction based on a few factors, prioritizing
|
|
// coercions and initializer disjunctions, followed by bind overload
|
|
// disjunctions with fewest unbound argument types.
|
|
Optional<Constraint *> selection;
|
|
Optional<unsigned> lowestUnbound;
|
|
Optional<unsigned> lowestActive;
|
|
|
|
for (auto candidate : disjunctions) {
|
|
unsigned countActive = candidate->countActiveNestedConstraints();
|
|
// Skip disjunctions with no active constraints.
|
|
if (!countActive)
|
|
continue;
|
|
|
|
// If this is the first disjunction with an active constraint,
|
|
// consider this our selection until we find a better one.
|
|
if (!selection)
|
|
selection = candidate;
|
|
|
|
if (auto loc = candidate->getLocator()) {
|
|
// If we have a coercion disjunction remaining, select that as
|
|
// they tend to constraint type variables maximally and give us
|
|
// information about potential bindings.
|
|
if (auto anchor = loc->getAnchor()) {
|
|
if (isa<CoerceExpr>(anchor)) {
|
|
selection = candidate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Initializers also provide a lot of information in the form of
|
|
// the result type and tend to split the system into independent
|
|
// pieces.
|
|
auto path = loc->getPath();
|
|
if (!path.empty() &&
|
|
path.back().getKind() ==
|
|
ConstraintLocator::PathElementKind::ConstructorMember) {
|
|
selection = candidate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Attempt to find a bind overload disjunction before considering
|
|
// this one.
|
|
assert(!candidate->getNestedConstraints().empty() &&
|
|
"Expected non-empty disjunction!");
|
|
if (candidate->getNestedConstraints().front()->getKind() !=
|
|
ConstraintKind::BindOverload)
|
|
continue;
|
|
|
|
// Otherwise, attempt to count the number of unbound types used
|
|
// for bind overload disjunctions.
|
|
unsigned countUnbound = countUnboundTypes(*this, candidate);
|
|
|
|
// If the number of unbound arguments is smaller than previous
|
|
// candidate, or if we had no previous candidate, this is our
|
|
// newest best option.
|
|
if (!lowestUnbound || countUnbound < lowestUnbound.getValue() ||
|
|
(countUnbound == lowestUnbound.getValue() &&
|
|
countActive < lowestActive.getValue())) {
|
|
selection = candidate;
|
|
lowestUnbound = countUnbound;
|
|
lowestActive = countActive;
|
|
}
|
|
}
|
|
|
|
// If there are no active constraints in the disjunction, there is
|
|
// no solution.
|
|
if (!selection)
|
|
return true;
|
|
|
|
auto *disjunction = selection.getValue();
|
|
|
|
// Remove this disjunction constraint from the list.
|
|
auto afterDisjunction = InactiveConstraints.erase(disjunction);
|
|
CG.removeConstraint(disjunction);
|
|
|
|
// Try each of the constraints within the disjunction.
|
|
Constraint *firstSolvedConstraint = nullptr;
|
|
Constraint *firstNonGenericOperatorSolution = nullptr;
|
|
++solverState->NumDisjunctions;
|
|
auto constraints = disjunction->getNestedConstraints();
|
|
for (auto index : indices(constraints)) {
|
|
auto constraint = constraints[index];
|
|
if (constraint->isDisabled()) {
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth)
|
|
<< "(skipping ";
|
|
constraint->print(log, &TC.Context.SourceMgr);
|
|
log << '\n';
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Don't attempt to solve for generic operators if we already have
|
|
// a non-generic solution.
|
|
|
|
// FIXME: Less-horrible but still horrible hack to attempt to
|
|
// speed things up. Skip the generic operators if we
|
|
// already have a solution involving non-generic operators,
|
|
// but continue looking for a better non-generic operator
|
|
// solution.
|
|
if (firstNonGenericOperatorSolution &&
|
|
isGenericOperatorOrUnavailable(constraint))
|
|
continue;
|
|
|
|
// We already have a solution; check whether we should
|
|
// short-circuit the disjunction.
|
|
if (firstSolvedConstraint &&
|
|
shortCircuitDisjunctionAt(constraint, firstSolvedConstraint))
|
|
break;
|
|
|
|
// If the expression was deemed "too complex", stop now and salvage.
|
|
if (getExpressionTooComplex(solutions))
|
|
break;
|
|
|
|
// Try to solve the system with this option in the disjunction.
|
|
SolverScope scope(*this);
|
|
++solverState->NumDisjunctionTerms;
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth)
|
|
<< "(assuming ";
|
|
constraint->print(log, &TC.Context.SourceMgr);
|
|
log << '\n';
|
|
}
|
|
|
|
// If the disjunction requested us to, remember which choice we
|
|
// took for it.
|
|
if (disjunction->shouldRememberChoice()) {
|
|
auto locator = disjunction->getLocator();
|
|
assert(locator && "remembered disjunction doesn't have a locator?");
|
|
DisjunctionChoices.push_back({locator, index});
|
|
}
|
|
|
|
// Simplify this term in the disjunction.
|
|
switch (simplifyConstraint(*constraint)) {
|
|
case SolutionKind::Error:
|
|
if (!failedConstraint)
|
|
failedConstraint = constraint;
|
|
solverState->retireConstraint(constraint);
|
|
break;
|
|
|
|
case SolutionKind::Solved:
|
|
solverState->retireConstraint(constraint);
|
|
break;
|
|
|
|
case SolutionKind::Unsolved:
|
|
InactiveConstraints.push_back(constraint);
|
|
CG.addConstraint(constraint);
|
|
break;
|
|
}
|
|
|
|
// Record this as a generated constraint.
|
|
solverState->addGeneratedConstraint(constraint);
|
|
|
|
if (!solveRec(solutions, allowFreeTypeVariables)) {
|
|
if (!firstNonGenericOperatorSolution &&
|
|
!isGenericOperatorOrUnavailable(constraint) &&
|
|
isSymmetricOperator(constraint))
|
|
firstNonGenericOperatorSolution = constraint;
|
|
|
|
firstSolvedConstraint = constraint;
|
|
|
|
// If we see a tuple-to-tuple conversion that succeeded, we're done.
|
|
// FIXME: This should be more general.
|
|
if (auto restriction = constraint->getRestriction()) {
|
|
if (*restriction == ConversionRestrictionKind::TupleToTuple)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TC.getLangOpts().DebugConstraintSolver) {
|
|
auto &log = getASTContext().TypeCheckerDebug->getStream();
|
|
log.indent(solverState->depth) << ")\n";
|
|
}
|
|
}
|
|
|
|
// Put the disjunction constraint back in its place.
|
|
InactiveConstraints.insert(afterDisjunction, disjunction);
|
|
CG.addConstraint(disjunction);
|
|
|
|
// If we are exiting due to an expression that is too complex, do
|
|
// not allow our caller to continue as if we have been successful.
|
|
// Maintain the broken behavior under Swift 3 mode though, to avoid
|
|
// breaking code.
|
|
auto tooComplex = getExpressionTooComplex(solutions) &&
|
|
!getASTContext().isSwiftVersion3();
|
|
|
|
return tooComplex || !firstSolvedConstraint;
|
|
}
|