mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
One difficulty in generating reasonable diagnostic data for type check failures has been the fact that many constraints had been synthesized without regard for where they were rooted in the program source. The result of this was that even though we would store failure information for specific constraints, we wouldn't emit it for lack of a source location. By making location data a non-optional component of constraints, we can begin diagnosing type check errors closer to their point of failure. Swift SVN r18751
3350 lines
120 KiB
C++
3350 lines
120 KiB
C++
//===--- CSSimplify.cpp - Constraint Simplification -----------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements simplifications of constraints within the constraint
|
|
// system.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ConstraintSystem.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
|
|
using namespace swift;
|
|
using namespace constraints;
|
|
|
|
static bool hasMandatoryTupleLabels(const ConstraintLocatorBuilder &locator) {
|
|
if (Expr *e = locator.trySimplifyToExpr())
|
|
return hasMandatoryTupleLabels(e);
|
|
return false;
|
|
}
|
|
|
|
MatchCallArgumentListener::~MatchCallArgumentListener() { }
|
|
|
|
void MatchCallArgumentListener::extraArgument(unsigned argIdx) { }
|
|
|
|
void MatchCallArgumentListener::missingArgument(unsigned paramIdx) { }
|
|
|
|
void MatchCallArgumentListener::outOfOrderArgument(unsigned argIdx,
|
|
unsigned prevArgIdx) {
|
|
}
|
|
|
|
bool MatchCallArgumentListener::relabelArguments(ArrayRef<Identifier> newNames){
|
|
return true;
|
|
}
|
|
|
|
/// Produce a score (smaller is better) comparing a parameter name and
|
|
/// potentially-typod argument name.
|
|
///
|
|
/// \param paramName The name of the parameter.
|
|
/// \param argName The name of the argument.
|
|
/// \param maxScore The maximum score permitted by this comparison, or
|
|
/// 0 if there is no limit.
|
|
///
|
|
/// \returns the score, if it is good enough to even consider this a match.
|
|
/// Otherwise, an empty optional.
|
|
///
|
|
static Optional<unsigned> scoreParamAndArgNameTypo(StringRef paramName,
|
|
StringRef argName,
|
|
unsigned maxScore) {
|
|
using namespace camel_case;
|
|
|
|
// If one of the names starts with "with", drop it and compare the rest.
|
|
bool argStartsWith = startsWithIgnoreFirstCase(argName, "with");
|
|
bool paramStartsWith = startsWithIgnoreFirstCase(paramName, "with");
|
|
if (argStartsWith != paramStartsWith) {
|
|
if (argStartsWith)
|
|
argName = argName.substr(4);
|
|
else
|
|
paramName = paramName.substr(4);
|
|
}
|
|
|
|
// Compute the edit distance.
|
|
unsigned dist = argName.edit_distance(paramName, /*AllowReplacements=*/true,
|
|
/*MaxEditDistance=*/maxScore);
|
|
|
|
// If the edit distance would be too long, we're done.
|
|
if (maxScore != 0 && dist > maxScore)
|
|
return Nothing;
|
|
|
|
// The distance can be zero due to the "with" transformation above.
|
|
if (dist == 0)
|
|
return 1;
|
|
|
|
// Only allow about one typo for every two properly-typed characters, which
|
|
// prevents completely-wacky suggestions in many cases.
|
|
if (dist > (argName.size() + 1) / 3)
|
|
return Nothing;
|
|
|
|
return dist;
|
|
}
|
|
|
|
/// Function that determines whether the parameter at the given
|
|
/// index can be treated as a trailing closure.
|
|
///
|
|
/// FIXME: This is the wrong question to ask. We should know when something is
|
|
/// a trailing closure.
|
|
static bool paramIsTrailingClosure(ArrayRef<TupleTypeElt> paramTuple,
|
|
unsigned paramIdx) {
|
|
for (unsigned i = paramIdx, n = paramTuple.size(); i != n; ++i) {
|
|
const auto ¶m = paramTuple[i];
|
|
auto type = param.isVararg() ? param.getVarargBaseTy() : param.getType();
|
|
type = type->getRValueInstanceType();
|
|
|
|
// Look through optionals.
|
|
if (auto optValue = type->getAnyOptionalObjectType()) {
|
|
type = optValue;
|
|
}
|
|
|
|
auto funcTy = type->getAs<FunctionType>();
|
|
if (!funcTy)
|
|
return false;
|
|
|
|
if (funcTy->isAutoClosure())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
ArrayRef<TupleTypeElt> constraints::decomposeArgParamType(Type type,
|
|
TupleTypeElt &scalar){
|
|
switch (type->getKind()) {
|
|
case TypeKind::Tuple:
|
|
return cast<TupleType>(type.getPointer())->getFields();
|
|
|
|
case TypeKind::Paren:
|
|
scalar = cast<ParenType>(type.getPointer())->getUnderlyingType();
|
|
return scalar;
|
|
|
|
default:
|
|
scalar = type;
|
|
return scalar;
|
|
}
|
|
}
|
|
|
|
bool constraints::matchCallArguments(
|
|
ArrayRef<TupleTypeElt> argTuple,
|
|
ArrayRef<TupleTypeElt> paramTuple,
|
|
bool allowFixes,
|
|
MatchCallArgumentListener &listener,
|
|
SmallVectorImpl<ParamBinding> ¶meterBindings) {
|
|
// Keep track of the parameter we're matching and what argument indices
|
|
// got bound to each parameter.
|
|
unsigned paramIdx, numParams = paramTuple.size();
|
|
parameterBindings.clear();
|
|
parameterBindings.resize(numParams);
|
|
|
|
// Keep track of which arguments we have claimed from the argument tuple.
|
|
unsigned nextArgIdx = 0, numArgs = argTuple.size();
|
|
SmallVector<bool, 4> claimedArgs(numArgs, false);
|
|
SmallVector<Identifier, 4> actualArgNames;
|
|
unsigned numClaimedArgs = 0;
|
|
|
|
// Indicates whether any of the arguments are potentially out-of-order,
|
|
// requiring further checking at the end.
|
|
bool potentiallyOutOfOrder = false;
|
|
|
|
// Local function that claims the argument at \c nextArgIdx, returning the
|
|
// index of the claimed argument. This is primarily a helper for
|
|
// \c claimNextNamed.
|
|
auto claim = [&](Identifier expectedName, bool ignoreNameClash = false)
|
|
-> unsigned {
|
|
// Make sure we can claim this argument.
|
|
assert(nextArgIdx != numArgs && "Must have a valid index to claim");
|
|
assert(!claimedArgs[nextArgIdx] && "Argument already claimed");
|
|
|
|
if (!actualArgNames.empty()) {
|
|
// We're recording argument names; record this one.
|
|
actualArgNames[nextArgIdx] = expectedName;
|
|
} else if (argTuple[nextArgIdx].getName() != expectedName &&
|
|
!ignoreNameClash) {
|
|
// We have an argument name mismatch. Start recording argument names.
|
|
actualArgNames.resize(numArgs);
|
|
|
|
// Figure out previous argument names from the parameter bindings.
|
|
for (unsigned i = 0; i != numParams; ++i) {
|
|
const auto ¶m = paramTuple[i];
|
|
bool firstArg = true;
|
|
|
|
for (auto argIdx : parameterBindings[i]) {
|
|
actualArgNames[argIdx] = firstArg ? param.getName() : Identifier();
|
|
firstArg = false;
|
|
}
|
|
}
|
|
|
|
// Record this argument name.
|
|
actualArgNames[nextArgIdx] = expectedName;
|
|
}
|
|
|
|
claimedArgs[nextArgIdx] = true;
|
|
++numClaimedArgs;
|
|
return nextArgIdx++;
|
|
};
|
|
|
|
// Local function that skips over any claimed arguments.
|
|
auto skipClaimedArgs = [&]() {
|
|
while (nextArgIdx != numArgs && claimedArgs[nextArgIdx])
|
|
++nextArgIdx;
|
|
};
|
|
|
|
// Local function that retrieves the next unclaimed argument with the given
|
|
// name (which may be empty). This routine claims the argument.
|
|
auto claimNextNamed
|
|
= [&](Identifier name, bool ignoreNameMismatch) -> Optional<unsigned> {
|
|
// If we've claimed all of the arguments, there's nothing more to do.
|
|
if (numClaimedArgs == numArgs)
|
|
return Nothing;
|
|
|
|
// When the expected name is empty, we claim the next argument if it has
|
|
// no name.
|
|
if (name.empty()) {
|
|
// Nothing to claim.
|
|
if (nextArgIdx == numArgs ||
|
|
claimedArgs[nextArgIdx] ||
|
|
(argTuple[nextArgIdx].hasName() && !ignoreNameMismatch))
|
|
return Nothing;
|
|
|
|
return claim(name);
|
|
}
|
|
|
|
// Skip over any claimed arguments.
|
|
skipClaimedArgs();
|
|
|
|
// If the name matches, claim this argument.
|
|
if (nextArgIdx != numArgs &&
|
|
(ignoreNameMismatch ||
|
|
argTuple[nextArgIdx].getName() == name)) {
|
|
return claim(name);
|
|
}
|
|
|
|
// The name didn't match. Go hunting for an unclaimed argument whose name
|
|
// does match.
|
|
Optional<unsigned> claimedWithSameName;
|
|
for (unsigned i = nextArgIdx; i != numArgs; ++i) {
|
|
// Skip arguments where the name doesn't match.
|
|
if (argTuple[i].getName() != name)
|
|
continue;
|
|
|
|
// Skip claimed arguments.
|
|
if (claimedArgs[i]) {
|
|
// Note that we have already claimed an argument with the same name.
|
|
if (!claimedWithSameName) {
|
|
claimedWithSameName = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// We found a match. Claim it.
|
|
nextArgIdx = i;
|
|
return claim(name);
|
|
}
|
|
|
|
// Look at previous arguments to determine whether any are unclaimed and
|
|
// have the same name.
|
|
for (unsigned i = 0; i != nextArgIdx; ++i) {
|
|
// Skip arguments where the name doesn't match.
|
|
if (argTuple[i].getName() != name)
|
|
continue;
|
|
|
|
// Skip claimed arguments.
|
|
if (claimedArgs[i]) {
|
|
// Note that we have already claimed an argument with the same name.
|
|
if (!claimedWithSameName) {
|
|
claimedWithSameName = i;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// We found a match. Claim it and note that the arguments are potentially
|
|
// out of order.
|
|
potentiallyOutOfOrder = true;
|
|
nextArgIdx = i;
|
|
return claim(name);
|
|
}
|
|
|
|
// If the current parameter appears to be a trailing closure, allow the
|
|
// argument to be unnamed even if the parameter has a name.
|
|
// FIXME: This is a hack. We should know whether it's a trailing closure
|
|
// or not.
|
|
if (nextArgIdx != numArgs && paramIsTrailingClosure(paramTuple, paramIdx) &&
|
|
argTuple[nextArgIdx].getName().empty()) {
|
|
return claim(Identifier(), /*ignoreNameClash=*/true);
|
|
}
|
|
|
|
// If we're not supposed to attempt any fixes, we're done.
|
|
if (!allowFixes)
|
|
return Nothing;
|
|
|
|
// Several things could have gone wrong here, and we'll check for each
|
|
// of them at some point:
|
|
// - The keyword argument might be redundant, in which case we can point
|
|
// out the issue.
|
|
// - The argument might be unnamed, in which case we try to fix the
|
|
// problem by adding the name.
|
|
// - The keyword argument might be a typo for an actual argument name, in
|
|
// which case we should find the closest match to correct to.
|
|
|
|
// Redundant keyword arguments.
|
|
if (claimedWithSameName) {
|
|
// FIXME: We can provide better diagnostics here.
|
|
return Nothing;
|
|
}
|
|
|
|
// Missing a keyword argument name.
|
|
if (nextArgIdx != numArgs && !argTuple[nextArgIdx].hasName()) {
|
|
// Claim the next argument.
|
|
return claim(name);
|
|
}
|
|
|
|
// Typo correction is handled in a later pass.
|
|
return Nothing;
|
|
};
|
|
|
|
// Local function that attempts to bind the given parameter to arguments in
|
|
// the list.
|
|
bool haveUnfulfilledParams = false;
|
|
auto bindNextParameter = [&](bool ignoreNameMismatch) {
|
|
const auto ¶m = paramTuple[paramIdx];
|
|
|
|
// Handle variadic parameters.
|
|
if (param.isVararg()) {
|
|
// Claim the next argument with the name of this parameter.
|
|
auto claimed = claimNextNamed(param.getName(), ignoreNameMismatch);
|
|
|
|
// If there was no such argument, leave the argument unf
|
|
if (!claimed) {
|
|
haveUnfulfilledParams = true;
|
|
return;
|
|
}
|
|
|
|
// Record the first argument for the variadic.
|
|
parameterBindings[paramIdx].push_back(*claimed);
|
|
|
|
// Claim any additional unnamed arguments.
|
|
while ((claimed = claimNextNamed(Identifier(), false))) {
|
|
parameterBindings[paramIdx].push_back(*claimed);
|
|
}
|
|
|
|
skipClaimedArgs();
|
|
return;
|
|
}
|
|
|
|
// Try to claim an argument for this parameter.
|
|
if (auto claimed = claimNextNamed(param.getName(), ignoreNameMismatch)) {
|
|
parameterBindings[paramIdx].push_back(*claimed);
|
|
skipClaimedArgs();
|
|
return;
|
|
}
|
|
|
|
// There was no argument to claim. Leave the argument unfulfilled.
|
|
haveUnfulfilledParams = true;
|
|
};
|
|
|
|
// Mark through the parameters, binding them to their arguments.
|
|
for (paramIdx = 0; paramIdx != numParams; ++paramIdx) {
|
|
bindNextParameter(false);
|
|
}
|
|
|
|
// If we have any unclaimed arguments, complain about those.
|
|
if (numClaimedArgs != numArgs) {
|
|
// If we're not allowed to fix anything, or if we don't have any
|
|
// unfulfilled parameters, fail.
|
|
if (!haveUnfulfilledParams || !allowFixes) {
|
|
nextArgIdx = 0;
|
|
skipClaimedArgs();
|
|
listener.extraArgument(nextArgIdx);
|
|
return true;
|
|
}
|
|
|
|
// Find all of the named, unclaimed arguments.
|
|
llvm::SmallVector<unsigned, 4> unclaimedNamedArgs;
|
|
for (nextArgIdx = 0; skipClaimedArgs(), nextArgIdx != numArgs;
|
|
++nextArgIdx) {
|
|
if (argTuple[nextArgIdx].hasName())
|
|
unclaimedNamedArgs.push_back(nextArgIdx);
|
|
}
|
|
|
|
if (!unclaimedNamedArgs.empty()) {
|
|
// Find all of the named, unfulfilled parameters.
|
|
llvm::SmallVector<unsigned, 4> unfulfilledNamedParams;
|
|
bool hasUnfulfilledUnnamedParams = false;
|
|
for (paramIdx = 0; paramIdx != numParams; ++paramIdx ) {
|
|
if (parameterBindings[paramIdx].empty()) {
|
|
if (paramTuple[paramIdx].hasName())
|
|
unfulfilledNamedParams.push_back(paramIdx);
|
|
else
|
|
hasUnfulfilledUnnamedParams = true;
|
|
}
|
|
}
|
|
|
|
if (!unfulfilledNamedParams.empty()) {
|
|
// Use typo correction to find the best matches.
|
|
// FIXME: There is undoubtedly a good dynamic-programming algorithm
|
|
// to find the best assignment here.
|
|
for (auto argIdx : unclaimedNamedArgs) {
|
|
auto argName = argTuple[argIdx].getName();
|
|
|
|
// Find the closest matching unfulfilled named parameter.
|
|
unsigned bestScore = 0;
|
|
unsigned best = 0;
|
|
for (unsigned i = 0, n = unfulfilledNamedParams.size(); i != n; ++i) {
|
|
unsigned param = unfulfilledNamedParams[i];
|
|
auto paramName = paramTuple[param].getName();
|
|
|
|
if (auto score = scoreParamAndArgNameTypo(paramName.str(),
|
|
argName.str(),
|
|
bestScore)) {
|
|
if (*score < bestScore || bestScore == 0) {
|
|
bestScore = *score;
|
|
best = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we found a parameter to fulfill, do it.
|
|
if (bestScore > 0) {
|
|
// Bind this parameter to the argument.
|
|
nextArgIdx = argIdx;
|
|
paramIdx = unfulfilledNamedParams[best];
|
|
bindNextParameter(true);
|
|
|
|
// Erase this parameter from the list of unfulfilled named
|
|
// parameters, so we don't try to fulfill it again.
|
|
unfulfilledNamedParams.erase(unfulfilledNamedParams.begin() + best);
|
|
if (unfulfilledNamedParams.empty())
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update haveUnfulfilledParams, because we may have fulfilled some
|
|
// parameters above.
|
|
haveUnfulfilledParams = hasUnfulfilledUnnamedParams ||
|
|
!unfulfilledNamedParams.empty();
|
|
}
|
|
}
|
|
|
|
// Find all of the unfulfilled parameters, and match them up
|
|
// semi-positionally.
|
|
if (numClaimedArgs != numArgs) {
|
|
// Restart at the first argument/parameter.
|
|
nextArgIdx = 0;
|
|
skipClaimedArgs();
|
|
haveUnfulfilledParams = false;
|
|
for (paramIdx = 0; paramIdx != numParams; ++paramIdx) {
|
|
// Skip fulfilled parameters.
|
|
if (!parameterBindings[paramIdx].empty())
|
|
continue;
|
|
|
|
bindNextParameter(true);
|
|
}
|
|
}
|
|
|
|
// If we still haven't claimed all of the arguments, fail.
|
|
if (numClaimedArgs != numArgs) {
|
|
nextArgIdx = 0;
|
|
skipClaimedArgs();
|
|
listener.extraArgument(nextArgIdx);
|
|
return true;
|
|
}
|
|
|
|
// FIXME: If we had the actual parameters and knew the body names, those
|
|
// matches would be best.
|
|
potentiallyOutOfOrder = true;
|
|
}
|
|
|
|
// If we have any unfulfilled parameters, check them now.
|
|
if (haveUnfulfilledParams) {
|
|
for (paramIdx = 0; paramIdx != numParams; ++paramIdx) {
|
|
// If we have a binding for this parameter, we're done.
|
|
if (!parameterBindings[paramIdx].empty())
|
|
continue;
|
|
|
|
const auto ¶m = paramTuple[paramIdx];
|
|
|
|
// Variadic parameters can be unfulfilled.
|
|
if (param.isVararg())
|
|
continue;
|
|
|
|
// Parameters with defaults can be unfilfilled.
|
|
if (param.getDefaultArgKind() != DefaultArgumentKind::None)
|
|
continue;
|
|
|
|
listener.missingArgument(paramIdx);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If any arguments were provided out-of-order, check whether we have
|
|
// violated any of the reordering rules.
|
|
if (potentiallyOutOfOrder) {
|
|
// Build a mapping from arguments to parameters.
|
|
SmallVector<unsigned, 4> argumentBindings(numArgs);
|
|
for(paramIdx = 0; paramIdx != numParams; ++paramIdx) {
|
|
for (auto argIdx : parameterBindings[paramIdx])
|
|
argumentBindings[argIdx] = paramIdx;
|
|
}
|
|
|
|
// Walk through the arguments, determining if any were bound to parameters
|
|
// out-of-order where it is not permitted.
|
|
unsigned prevParamIdx = argumentBindings[0];
|
|
for (unsigned argIdx = 1; argIdx != numArgs; ++argIdx) {
|
|
unsigned paramIdx = argumentBindings[argIdx];
|
|
|
|
// If this argument binds to the same parameter as the previous one or to
|
|
// a later parameter, just update the parameter index.
|
|
if (paramIdx >= prevParamIdx) {
|
|
prevParamIdx = paramIdx;
|
|
continue;
|
|
}
|
|
|
|
// The argument binds to a parameter that comes earlier than the
|
|
// previous argument. This is fine so long as this parameter and all of
|
|
// those parameters up to (and including) the previously-bound parameter
|
|
// are either variadic or have a default argument.
|
|
for (unsigned i = paramIdx; i != prevParamIdx + 1; ++i) {
|
|
const auto ¶m = paramTuple[i];
|
|
if (param.isVararg() ||
|
|
param.getDefaultArgKind() != DefaultArgumentKind::None)
|
|
continue;
|
|
|
|
unsigned prevArgIdx = parameterBindings[prevParamIdx].front();
|
|
listener.outOfOrderArgument(argIdx, prevArgIdx);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no arguments were renamed, the call arguments match up with the
|
|
// parameters.
|
|
if (actualArgNames.empty())
|
|
return false;
|
|
|
|
// The arguments were relabeled; notify the listener.
|
|
return listener.relabelArguments(actualArgNames);
|
|
}
|
|
|
|
/// Determine whether we should attempt a user-defined conversion.
|
|
static bool shouldTryUserConversion(ConstraintSystem &cs, Type type) {
|
|
// Strip the l-value qualifier if present.
|
|
type = type->getRValueType();
|
|
|
|
// If this isn't a type that can have user-defined conversions, there's
|
|
// nothing to do.
|
|
if (!type->getNominalOrBoundGenericNominal() && !type->is<ArchetypeType>())
|
|
return false;
|
|
|
|
// If there are no user-defined conversions, there's nothing to do.
|
|
// FIXME: lame name!
|
|
auto &ctx = cs.getASTContext();
|
|
auto name = ctx.Id_Conversion;
|
|
return static_cast<bool>(cs.lookupMember(type, name));
|
|
}
|
|
|
|
// Match the argument of a call to the parameter.
|
|
static ConstraintSystem::SolutionKind
|
|
matchCallArguments(ConstraintSystem &cs, TypeMatchKind kind,
|
|
Type argType, Type paramType,
|
|
ConstraintLocatorBuilder locator) {
|
|
/// Listener used to produce failures if they should be produced.
|
|
class Listener : public MatchCallArgumentListener {
|
|
ConstraintSystem &CS;
|
|
Type ArgType;
|
|
Type ParamType;
|
|
ConstraintLocatorBuilder &Locator;
|
|
|
|
public:
|
|
Listener(ConstraintSystem &cs, Type argType, Type paramType,
|
|
ConstraintLocatorBuilder &locator)
|
|
: CS(cs), ArgType(argType), ParamType(paramType), Locator(locator) { }
|
|
|
|
virtual void extraArgument(unsigned argIdx) {
|
|
if (!CS.shouldRecordFailures())
|
|
return;
|
|
|
|
CS.recordFailure(CS.getConstraintLocator(Locator),
|
|
Failure::ExtraArgument,
|
|
argIdx);
|
|
}
|
|
|
|
virtual void missingArgument(unsigned paramIdx) {
|
|
if (!CS.shouldRecordFailures())
|
|
return;
|
|
|
|
CS.recordFailure(CS.getConstraintLocator(Locator),
|
|
Failure::MissingArgument,
|
|
ParamType, paramIdx);
|
|
}
|
|
|
|
virtual void outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) {
|
|
if (!CS.shouldRecordFailures())
|
|
return;
|
|
|
|
CS.recordFailure(CS.getConstraintLocator(Locator),
|
|
Failure::OutOfOrderArgument,
|
|
argIdx, prevArgIdx);
|
|
}
|
|
|
|
virtual bool relabelArguments(ArrayRef<Identifier> newNames) {
|
|
if (!CS.shouldAttemptFixes()) {
|
|
// FIXME: record why this failed. We have renaming.
|
|
return true;
|
|
}
|
|
|
|
// Add a fix for the name. This is only responsible for recording the fix.
|
|
auto result = CS.simplifyFixConstraint(
|
|
Fix::getRelabelTuple(CS, FixKind::RelabelCallTuple,
|
|
newNames),
|
|
ArgType, ParamType,
|
|
TypeMatchKind::ArgumentTupleConversion,
|
|
ConstraintSystem::TMF_None,
|
|
Locator);
|
|
return result != ConstraintSystem::SolutionKind::Solved;
|
|
}
|
|
};
|
|
|
|
// Extract the argument tuple fields.
|
|
TupleTypeElt argScalar;
|
|
ArrayRef<TupleTypeElt> argTuple = decomposeArgParamType(argType, argScalar);
|
|
|
|
// Extract the parameter tuple fields.
|
|
TupleTypeElt paramScalar;
|
|
ArrayRef<TupleTypeElt> paramTuple = decomposeArgParamType(paramType,
|
|
paramScalar);
|
|
|
|
// Match up the call arguments to the parameters.
|
|
Listener listener(cs, argType, paramType, locator);
|
|
SmallVector<ParamBinding, 4> parameterBindings;
|
|
if (constraints::matchCallArguments(argTuple, paramTuple,
|
|
cs.shouldAttemptFixes(), listener,
|
|
parameterBindings))
|
|
return ConstraintSystem::SolutionKind::Error;
|
|
|
|
// Check the argument types for each of the parameters.
|
|
unsigned subflags = ConstraintSystem::TMF_GenerateConstraints;
|
|
TypeMatchKind subKind;
|
|
switch (kind) {
|
|
case TypeMatchKind::ArgumentTupleConversion:
|
|
subKind = TypeMatchKind::Conversion;
|
|
break;
|
|
|
|
case TypeMatchKind::OperatorArgumentTupleConversion:
|
|
subKind = TypeMatchKind::OperatorArgumentConversion;
|
|
break;
|
|
|
|
case TypeMatchKind::OperatorArgumentConversion:
|
|
case TypeMatchKind::Conversion:
|
|
case TypeMatchKind::BindType:
|
|
case TypeMatchKind::SameType:
|
|
case TypeMatchKind::Subtype:
|
|
llvm_unreachable("Not an call argument constraint");
|
|
}
|
|
|
|
auto haveOneNonUserConversion =
|
|
(subKind != TypeMatchKind::OperatorArgumentConversion);
|
|
|
|
for (unsigned paramIdx = 0, numParams = parameterBindings.size();
|
|
paramIdx != numParams; ++paramIdx){
|
|
// Skip unfulfilled parameters. There's nothing to do for them.
|
|
if (parameterBindings[paramIdx].empty())
|
|
continue;
|
|
|
|
// Determine the parameter type.
|
|
const auto ¶m = paramTuple[paramIdx];
|
|
auto paramTy = param.isVararg() ? param.getVarargBaseTy()
|
|
: param.getType();
|
|
|
|
// Compare each of the bound arguments for this parameter.
|
|
for (auto argIdx : parameterBindings[paramIdx]) {
|
|
auto loc = locator.withPathElement(LocatorPathElt::
|
|
getApplyArgToParam(argIdx,
|
|
paramIdx));
|
|
auto argTy = argTuple[argIdx].getType();
|
|
|
|
|
|
auto isOptionalConversion = false;
|
|
auto notTyVarMatch = false;
|
|
|
|
if (!haveOneNonUserConversion) {
|
|
notTyVarMatch = !(isa<TypeVariableType>(argTy->getDesugaredType()) ||
|
|
isa<TypeVariableType>(paramTy->getDesugaredType()));
|
|
|
|
// We'll bypass optional conversions because of the implicit conversions
|
|
// that take place - there will always be a ValueToOptional disjunctive
|
|
// choice.
|
|
if (notTyVarMatch) {
|
|
if(auto boundGenericType = paramTy->getAs<BoundGenericType>()) {
|
|
auto typeDecl = boundGenericType->getDecl();
|
|
isOptionalConversion =
|
|
typeDecl->classifyAsOptionalType() ==
|
|
OTK_ImplicitlyUnwrappedOptional;
|
|
}
|
|
}
|
|
|
|
haveOneNonUserConversion = (!notTyVarMatch && !paramIdx) ||
|
|
isOptionalConversion ||
|
|
haveOneNonUserConversion;
|
|
}
|
|
|
|
// If at least one operator argument can be applied to without a user
|
|
// conversion, there's no need to check the others.
|
|
if (!haveOneNonUserConversion && notTyVarMatch) {
|
|
auto applyFlags = subflags |
|
|
ConstraintSystem::TMF_ApplyingOperatorParameter;
|
|
auto nonConversionResult = cs.matchTypes(argTy,
|
|
paramTy,
|
|
subKind,
|
|
applyFlags,
|
|
loc);
|
|
if (nonConversionResult == ConstraintSystem::SolutionKind::Solved) {
|
|
haveOneNonUserConversion = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (cs.matchTypes(argTy,paramTy,
|
|
subKind, subflags,
|
|
loc)) {
|
|
case ConstraintSystem::SolutionKind::Error:
|
|
return ConstraintSystem::SolutionKind::Error;
|
|
|
|
case ConstraintSystem::SolutionKind::Solved:
|
|
case ConstraintSystem::SolutionKind::Unsolved:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!haveOneNonUserConversion) {
|
|
return ConstraintSystem::SolutionKind::Error;
|
|
}
|
|
|
|
return ConstraintSystem::SolutionKind::Solved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
|
|
TypeMatchKind kind, unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
unsigned subFlags = flags | TMF_GenerateConstraints;
|
|
|
|
// Equality and subtyping have fairly strict requirements on tuple matching,
|
|
// requiring element names to either match up or be disjoint.
|
|
if (kind < TypeMatchKind::Conversion) {
|
|
if (tuple1->getFields().size() != tuple2->getFields().size()) {
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::TupleSizeMismatch, tuple1, tuple2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
for (unsigned i = 0, n = tuple1->getFields().size(); i != n; ++i) {
|
|
const auto &elt1 = tuple1->getFields()[i];
|
|
const auto &elt2 = tuple2->getFields()[i];
|
|
|
|
// If the names don't match, we may have a conflict.
|
|
if (elt1.getName() != elt2.getName()) {
|
|
// Same-type requirements require exact name matches.
|
|
if (kind == TypeMatchKind::SameType) {
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(
|
|
locator.withPathElement(
|
|
LocatorPathElt::getNamedTupleElement(i))),
|
|
Failure::TupleNameMismatch, tuple1, tuple2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// For subtyping constraints, just make sure that this name isn't
|
|
// used at some other position.
|
|
if (elt2.hasName()) {
|
|
int matched = tuple1->getNamedElementId(elt2.getName());
|
|
if (matched != -1) {
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(
|
|
locator.withPathElement(
|
|
LocatorPathElt::getNamedTupleElement(i))),
|
|
Failure::TupleNamePositionMismatch, tuple1, tuple2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Variadic bit must match.
|
|
if (elt1.isVararg() != elt2.isVararg()) {
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(
|
|
locator.withPathElement(
|
|
LocatorPathElt::getNamedTupleElement(i))),
|
|
Failure::TupleVariadicMismatch, tuple1, tuple2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Compare the element types.
|
|
switch (matchTypes(elt1.getType(), elt2.getType(), kind, subFlags,
|
|
locator.withPathElement(
|
|
LocatorPathElt::getTupleElement(i)))) {
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case SolutionKind::Solved:
|
|
case SolutionKind::Unsolved:
|
|
break;
|
|
}
|
|
}
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
assert(kind >= TypeMatchKind::Conversion);
|
|
TypeMatchKind subKind;
|
|
switch (kind) {
|
|
case TypeMatchKind::ArgumentTupleConversion:
|
|
subKind = TypeMatchKind::Conversion;
|
|
break;
|
|
|
|
case TypeMatchKind::OperatorArgumentTupleConversion:
|
|
subKind = TypeMatchKind::OperatorArgumentConversion;
|
|
break;
|
|
|
|
case TypeMatchKind::OperatorArgumentConversion:
|
|
case TypeMatchKind::Conversion:
|
|
subKind = TypeMatchKind::Conversion;
|
|
break;
|
|
|
|
case TypeMatchKind::BindType:
|
|
case TypeMatchKind::SameType:
|
|
case TypeMatchKind::Subtype:
|
|
llvm_unreachable("Not a conversion");
|
|
}
|
|
|
|
// Compute the element shuffles for conversions.
|
|
SmallVector<int, 16> sources;
|
|
SmallVector<unsigned, 4> variadicArguments;
|
|
if (computeTupleShuffle(tuple1, tuple2, sources, variadicArguments,
|
|
::hasMandatoryTupleLabels(locator))) {
|
|
// FIXME: Record why the tuple shuffle couldn't be computed.
|
|
if (shouldRecordFailures()) {
|
|
if (tuple1->getNumElements() != tuple2->getNumElements()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::TupleSizeMismatch, tuple1, tuple2);
|
|
}
|
|
}
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Check each of the elements.
|
|
bool hasVarArg = false;
|
|
for (unsigned idx2 = 0, n = sources.size(); idx2 != n; ++idx2) {
|
|
// Default-initialization always allowed for conversions.
|
|
if (sources[idx2] == TupleShuffleExpr::DefaultInitialize) {
|
|
continue;
|
|
}
|
|
|
|
// Variadic arguments handled below.
|
|
if (sources[idx2] == TupleShuffleExpr::FirstVariadic) {
|
|
hasVarArg = true;
|
|
continue;
|
|
}
|
|
|
|
assert(sources[idx2] >= 0);
|
|
unsigned idx1 = sources[idx2];
|
|
|
|
// Match up the types.
|
|
const auto &elt1 = tuple1->getFields()[idx1];
|
|
const auto &elt2 = tuple2->getFields()[idx2];
|
|
switch (matchTypes(elt1.getType(), elt2.getType(), subKind, subFlags,
|
|
locator.withPathElement(
|
|
LocatorPathElt::getTupleElement(idx1)))) {
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case SolutionKind::Solved:
|
|
case SolutionKind::Unsolved:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// If we have variadic arguments to check, do so now.
|
|
if (hasVarArg) {
|
|
const auto &elt2 = tuple2->getFields().back();
|
|
auto eltType2 = elt2.getVarargBaseTy();
|
|
|
|
for (unsigned idx1 : variadicArguments) {
|
|
switch (matchTypes(tuple1->getElementType(idx1), eltType2, subKind,
|
|
subFlags,
|
|
locator.withPathElement(
|
|
LocatorPathElt::getTupleElement(idx1)))) {
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case SolutionKind::Solved:
|
|
case SolutionKind::Unsolved:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchScalarToTupleTypes(Type type1, TupleType *tuple2,
|
|
TypeMatchKind kind, unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
int scalarFieldIdx = tuple2->getFieldForScalarInit();
|
|
assert(scalarFieldIdx >= 0 && "Invalid tuple for scalar-to-tuple");
|
|
const auto &elt = tuple2->getFields()[scalarFieldIdx];
|
|
auto scalarFieldTy = elt.isVararg()? elt.getVarargBaseTy() : elt.getType();
|
|
return matchTypes(type1, scalarFieldTy, kind, flags,
|
|
locator.withPathElement(ConstraintLocator::ScalarToTuple));
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchTupleToScalarTypes(TupleType *tuple1, Type type2,
|
|
TypeMatchKind kind, unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
assert(tuple1->getNumElements() == 1 && "Wrong number of elements");
|
|
assert(!tuple1->getFields()[0].isVararg() && "Should not be variadic");
|
|
|
|
return matchTypes(tuple1->getElementType(0),
|
|
type2, kind, flags,
|
|
locator.withPathElement(
|
|
LocatorPathElt::getTupleElement(0)));
|
|
}
|
|
|
|
/// Determine whether this is a function type accepting no arguments as input.
|
|
static bool isFunctionTypeAcceptingNoArguments(Type type) {
|
|
// The type must be a function type.
|
|
auto fnType = type->getAs<AnyFunctionType>();
|
|
if (!fnType)
|
|
return false;
|
|
|
|
// Only tuple arguments make sense.
|
|
auto tupleTy = fnType->getInput()->getAs<TupleType>();
|
|
if (!tupleTy) return false;
|
|
|
|
// Look for any non-defaulted, non-variadic elements.
|
|
for (const auto &elt : tupleTy->getFields()) {
|
|
// Defaulted arguments are okay.
|
|
if (elt.getDefaultArgKind() != DefaultArgumentKind::None)
|
|
continue;
|
|
|
|
// Variadic arguments are okay.
|
|
if (elt.isVararg())
|
|
continue;
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
|
|
TypeMatchKind kind, unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
// An @auto_closure function type can be a subtype of a
|
|
// non-@auto_closure function type.
|
|
if (func1->isAutoClosure() != func2->isAutoClosure()) {
|
|
if (func2->isAutoClosure() || kind < TypeMatchKind::Subtype) {
|
|
// If the second type is an autoclosure and the first type appears to
|
|
// accept no arguments, try to add the ().
|
|
if (func2->isAutoClosure() && shouldAttemptFixes() &&
|
|
kind >= TypeMatchKind::Conversion &&
|
|
isFunctionTypeAcceptingNoArguments(func1)) {
|
|
unsigned subFlags = (flags | TMF_GenerateConstraints) & ~TMF_ApplyingFix;
|
|
return simplifyFixConstraint(FixKind::NullaryCall, func1, func2, kind,
|
|
subFlags, locator);
|
|
}
|
|
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::FunctionAutoclosureMismatch, func1, func2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
}
|
|
|
|
// A @noreturn function type can be a subtype of a non-@noreturn function
|
|
// type.
|
|
if (func1->isNoReturn() != func2->isNoReturn()) {
|
|
if (func2->isNoReturn() || kind < TypeMatchKind::SameType) {
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::FunctionNoReturnMismatch, func1, func2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
}
|
|
|
|
// Determine how we match up the input/result types.
|
|
TypeMatchKind subKind;
|
|
switch (kind) {
|
|
case TypeMatchKind::BindType:
|
|
case TypeMatchKind::SameType:
|
|
subKind = kind;
|
|
break;
|
|
|
|
case TypeMatchKind::Subtype:
|
|
case TypeMatchKind::Conversion:
|
|
case TypeMatchKind::ArgumentTupleConversion:
|
|
case TypeMatchKind::OperatorArgumentTupleConversion:
|
|
case TypeMatchKind::OperatorArgumentConversion:
|
|
subKind = TypeMatchKind::Subtype;
|
|
break;
|
|
}
|
|
|
|
unsigned subFlags = flags | TMF_GenerateConstraints;
|
|
|
|
// Input types can be contravariant (or equal).
|
|
SolutionKind result = matchTypes(func2->getInput(), func1->getInput(),
|
|
subKind, subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::FunctionArgument));
|
|
if (result == SolutionKind::Error)
|
|
return SolutionKind::Error;
|
|
|
|
// Result type can be covariant (or equal).
|
|
switch (matchTypes(func1->getResult(), func2->getResult(), subKind,
|
|
subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::FunctionResult))) {
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case SolutionKind::Solved:
|
|
result = SolutionKind::Solved;
|
|
break;
|
|
|
|
case SolutionKind::Unsolved:
|
|
result = SolutionKind::Unsolved;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// \brief Map a failed type-matching kind to a failure kind, generically.
|
|
static Failure::FailureKind getRelationalFailureKind(TypeMatchKind kind) {
|
|
switch (kind) {
|
|
case TypeMatchKind::BindType:
|
|
case TypeMatchKind::SameType:
|
|
return Failure::TypesNotEqual;
|
|
|
|
case TypeMatchKind::Subtype:
|
|
return Failure::TypesNotSubtypes;
|
|
|
|
case TypeMatchKind::Conversion:
|
|
case TypeMatchKind::OperatorArgumentConversion:
|
|
case TypeMatchKind::ArgumentTupleConversion:
|
|
case TypeMatchKind::OperatorArgumentTupleConversion:
|
|
return Failure::TypesNotConvertible;
|
|
}
|
|
|
|
llvm_unreachable("unhandled type matching kind");
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchSuperclassTypes(Type type1, Type type2,
|
|
TypeMatchKind kind, unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
auto classDecl2 = type2->getClassOrBoundGenericClass();
|
|
bool done = false;
|
|
for (auto super1 = TC.getSuperClassOf(type1);
|
|
!done && super1;
|
|
super1 = TC.getSuperClassOf(super1)) {
|
|
if (super1->getClassOrBoundGenericClass() != classDecl2)
|
|
continue;
|
|
|
|
return matchTypes(super1, type2, TypeMatchKind::SameType,
|
|
TMF_GenerateConstraints, locator);
|
|
}
|
|
|
|
// Record this failure.
|
|
// FIXME: Specialize diagnostic.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
getRelationalFailureKind(kind), type1, type2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Handle nominal types that are not directly generic.
|
|
if (auto nominal1 = type1->getAs<NominalType>()) {
|
|
auto nominal2 = type2->castTo<NominalType>();
|
|
|
|
assert((bool)nominal1->getParent() == (bool)nominal2->getParent() &&
|
|
"Mismatched parents of nominal types");
|
|
|
|
if (!nominal1->getParent())
|
|
return SolutionKind::Solved;
|
|
|
|
// Match up the parents, exactly.
|
|
return matchTypes(nominal1->getParent(), nominal2->getParent(),
|
|
TypeMatchKind::SameType, TMF_GenerateConstraints,
|
|
locator.withPathElement(ConstraintLocator::ParentType));
|
|
}
|
|
|
|
auto bound1 = type1->castTo<BoundGenericType>();
|
|
auto bound2 = type2->castTo<BoundGenericType>();
|
|
|
|
// Match up the parents, exactly, if there are parents.
|
|
assert((bool)bound1->getParent() == (bool)bound2->getParent() &&
|
|
"Mismatched parents of bound generics");
|
|
if (bound1->getParent()) {
|
|
switch (matchTypes(bound1->getParent(), bound2->getParent(),
|
|
TypeMatchKind::SameType, TMF_GenerateConstraints,
|
|
locator.withPathElement(ConstraintLocator::ParentType))){
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case SolutionKind::Solved:
|
|
case SolutionKind::Unsolved:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Match up the generic arguments, exactly.
|
|
auto args1 = bound1->getGenericArgs();
|
|
auto args2 = bound2->getGenericArgs();
|
|
assert(args1.size() == args2.size() && "Mismatched generic args");
|
|
for (unsigned i = 0, n = args1.size(); i != n; ++i) {
|
|
switch (matchTypes(args1[i], args2[i], TypeMatchKind::SameType,
|
|
TMF_GenerateConstraints,
|
|
locator.withPathElement(
|
|
LocatorPathElt::getGenericArgument(i)))) {
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case SolutionKind::Solved:
|
|
case SolutionKind::Unsolved:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
|
|
TypeMatchKind kind, unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
SmallVector<ProtocolDecl *, 4> protocols;
|
|
type2->getAnyExistentialTypeProtocols(protocols);
|
|
|
|
for (auto proto : protocols) {
|
|
switch (simplifyConformsToConstraint(type1, proto, locator, flags, false)) {
|
|
case SolutionKind::Solved:
|
|
break;
|
|
|
|
case SolutionKind::Unsolved:
|
|
// Add the constraint.
|
|
addConstraint(ConstraintKind::ConformsTo, type1,
|
|
proto->getDeclaredType(), getConstraintLocator(rootExpr));
|
|
break;
|
|
|
|
case SolutionKind::Error:
|
|
return SolutionKind::Error;
|
|
}
|
|
}
|
|
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
/// \brief Map a type-matching kind to a constraint kind.
|
|
static ConstraintKind getConstraintKind(TypeMatchKind kind) {
|
|
switch (kind) {
|
|
case TypeMatchKind::BindType:
|
|
return ConstraintKind::Bind;
|
|
|
|
case TypeMatchKind::SameType:
|
|
return ConstraintKind::Equal;
|
|
|
|
case TypeMatchKind::Subtype:
|
|
return ConstraintKind::Subtype;
|
|
|
|
case TypeMatchKind::Conversion:
|
|
return ConstraintKind::Conversion;
|
|
|
|
case TypeMatchKind::ArgumentTupleConversion:
|
|
return ConstraintKind::ArgumentTupleConversion;
|
|
|
|
case TypeMatchKind::OperatorArgumentTupleConversion:
|
|
return ConstraintKind::OperatorArgumentTupleConversion;
|
|
|
|
case TypeMatchKind::OperatorArgumentConversion:
|
|
return ConstraintKind::OperatorArgumentConversion;
|
|
}
|
|
|
|
llvm_unreachable("unhandled type matching kind");
|
|
}
|
|
|
|
/// If the given type has user-defined conversions, introduce new
|
|
/// relational constraint between the result of performing the user-defined
|
|
/// conversion and an arbitrary other type.
|
|
static ConstraintSystem::SolutionKind
|
|
tryUserConversion(ConstraintSystem &cs, Type type, ConstraintKind kind,
|
|
Type otherType, ConstraintLocatorBuilder locator) {
|
|
assert(kind != ConstraintKind::Construction &&
|
|
kind != ConstraintKind::Conversion &&
|
|
kind != ConstraintKind::ArgumentTupleConversion &&
|
|
kind != ConstraintKind::OperatorArgumentTupleConversion &&
|
|
kind != ConstraintKind::OperatorArgumentConversion &&
|
|
"Construction/conversion constraints create potential cycles");
|
|
|
|
// If this isn't a type that can have user-defined conversions, there's
|
|
// nothing to do.
|
|
if (!shouldTryUserConversion(cs, type))
|
|
return ConstraintSystem::SolutionKind::Unsolved;
|
|
|
|
auto memberLocator = cs.getConstraintLocator(
|
|
locator.withPathElement(
|
|
ConstraintLocator::ConversionMember));
|
|
auto inputTV = cs.createTypeVariable(
|
|
cs.getConstraintLocator(memberLocator,
|
|
ConstraintLocator::ApplyArgument),
|
|
/*options=*/0);
|
|
auto outputTV = cs.createTypeVariable(
|
|
cs.getConstraintLocator(memberLocator,
|
|
ConstraintLocator::ApplyFunction),
|
|
/*options=*/0);
|
|
|
|
auto &ctx = cs.getASTContext();
|
|
auto name = ctx.Id_Conversion;
|
|
|
|
// The conversion function will have function type TI -> TO, for fresh
|
|
// type variables TI and TO.
|
|
cs.addValueMemberConstraint(type, name,
|
|
FunctionType::get(inputTV, outputTV),
|
|
memberLocator);
|
|
|
|
// A conversion function must accept an empty parameter list ().
|
|
// Note: This should never fail, because the declaration checker
|
|
// should ensure that conversions have no non-defaulted parameters.
|
|
cs.addConstraint(ConstraintKind::ArgumentTupleConversion,
|
|
TupleType::getEmpty(ctx),
|
|
inputTV, cs.getConstraintLocator(locator));
|
|
|
|
// Relate the output of the conversion function to the other type, using
|
|
// the provided constraint kind.
|
|
// If the type we're converting to is existential, we can also have an
|
|
// existential conversion here, so introduce a disjunction.
|
|
auto resultLocator = cs.getConstraintLocator(
|
|
locator.withPathElement(
|
|
ConstraintLocator::ConversionResult));
|
|
if (otherType->isExistentialType()) {
|
|
Constraint *constraints[2] = {
|
|
Constraint::create(cs, kind, outputTV, otherType, Identifier(),
|
|
resultLocator),
|
|
Constraint::createRestricted(cs, ConstraintKind::Conversion,
|
|
ConversionRestrictionKind::Existential,
|
|
outputTV, otherType, resultLocator)
|
|
};
|
|
cs.addConstraint(Constraint::createDisjunction(cs, constraints,
|
|
resultLocator));
|
|
} else {
|
|
cs.addConstraint(kind, outputTV, otherType, resultLocator);
|
|
}
|
|
|
|
// We're adding a user-defined conversion.
|
|
cs.increaseScore(SK_UserConversion);
|
|
|
|
return ConstraintSystem::SolutionKind::Solved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::matchTypes(Type type1, Type type2, TypeMatchKind kind,
|
|
unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
// If we have type variables that have been bound to fixed types, look through
|
|
// to the fixed type.
|
|
TypeVariableType *typeVar1;
|
|
bool isArgumentTupleConversion
|
|
= getASTContext().LangOpts.StrictKeywordArguments &&
|
|
(kind == TypeMatchKind::ArgumentTupleConversion ||
|
|
kind == TypeMatchKind::OperatorArgumentTupleConversion);
|
|
type1 = getFixedTypeRecursive(type1, typeVar1,
|
|
kind == TypeMatchKind::SameType,
|
|
isArgumentTupleConversion);
|
|
auto desugar1 = type1->getDesugaredType();
|
|
|
|
TypeVariableType *typeVar2;
|
|
type2 = getFixedTypeRecursive(type2, typeVar2,
|
|
kind == TypeMatchKind::SameType,
|
|
isArgumentTupleConversion);
|
|
auto desugar2 = type2->getDesugaredType();
|
|
|
|
// If the types are obviously equivalent, we're done.
|
|
if (desugar1 == desugar2)
|
|
return SolutionKind::Solved;
|
|
|
|
// If either (or both) types are type variables, unify the type variables.
|
|
if (typeVar1 || typeVar2) {
|
|
switch (kind) {
|
|
case TypeMatchKind::BindType:
|
|
case TypeMatchKind::SameType: {
|
|
if (typeVar1 && typeVar2) {
|
|
auto rep1 = getRepresentative(typeVar1);
|
|
auto rep2 = getRepresentative(typeVar2);
|
|
if (rep1 == rep2) {
|
|
// We already merged these two types, so this constraint is
|
|
// trivially solved.
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// If exactly one of the type variables can bind to an lvalue, we
|
|
// can't merge these two type variables.
|
|
if (rep1->getImpl().canBindToLValue()
|
|
!= rep2->getImpl().canBindToLValue()) {
|
|
if (flags & TMF_GenerateConstraints) {
|
|
// Add a new constraint between these types. We consider the current
|
|
// type-matching problem to the "solved" by this addition, because
|
|
// this new constraint will be solved at a later point.
|
|
// Obviously, this must not happen at the top level, or the
|
|
// algorithm would not terminate.
|
|
addConstraint(getConstraintKind(kind), rep1, rep2,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
return SolutionKind::Unsolved;
|
|
}
|
|
|
|
// Merge the equivalence classes corresponding to these two variables.
|
|
mergeEquivalenceClasses(rep1, rep2);
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// Provide a fixed type for the type variable.
|
|
bool wantRvalue = kind == TypeMatchKind::SameType;
|
|
if (typeVar1) {
|
|
// If we want an rvalue, get the rvalue.
|
|
if (wantRvalue)
|
|
type2 = type2->getRValueType();
|
|
|
|
// If the left-hand type variable cannot bind to an lvalue,
|
|
// but we still have an lvalue, fail.
|
|
if (!typeVar1->getImpl().canBindToLValue()) {
|
|
if (type2->is<LValueType>()) {
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::IsForbiddenLValue, type1, type2);
|
|
}
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Okay. Bind below.
|
|
}
|
|
|
|
assignFixedType(typeVar1, type2);
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// If we want an rvalue, get the rvalue.
|
|
if (wantRvalue)
|
|
type1 = type1->getRValueType();
|
|
|
|
if (!typeVar2->getImpl().canBindToLValue()) {
|
|
if (type1->is<LValueType>()) {
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::IsForbiddenLValue, type1, type2);
|
|
}
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Okay. Bind below.
|
|
}
|
|
|
|
assignFixedType(typeVar2, type1);
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
case TypeMatchKind::Subtype:
|
|
case TypeMatchKind::Conversion:
|
|
case TypeMatchKind::ArgumentTupleConversion:
|
|
case TypeMatchKind::OperatorArgumentTupleConversion:
|
|
case TypeMatchKind::OperatorArgumentConversion:
|
|
if (flags & TMF_GenerateConstraints) {
|
|
// Add a new constraint between these types. We consider the current
|
|
// type-matching problem to the "solved" by this addition, because
|
|
// this new constraint will be solved at a later point.
|
|
// Obviously, this must not happen at the top level, or the algorithm
|
|
// would not terminate.
|
|
addConstraint(getConstraintKind(kind), type1, type2,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// We couldn't solve this constraint. If only one of the types is a type
|
|
// variable, perhaps we can do something with it below.
|
|
if (typeVar1 && typeVar2)
|
|
return typeVar1 == typeVar2 ? SolutionKind::Solved
|
|
: SolutionKind::Unsolved;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
llvm::SmallVector<RestrictionOrFix, 4> conversionsOrFixes;
|
|
bool concrete = !typeVar1 && !typeVar2;
|
|
|
|
// If this is an argument conversion, handle it directly. The rules are
|
|
// different than for normal conversions.
|
|
if ((kind == TypeMatchKind::ArgumentTupleConversion ||
|
|
kind == TypeMatchKind::OperatorArgumentTupleConversion) &&
|
|
getASTContext().LangOpts.StrictKeywordArguments) {
|
|
if (concrete) {
|
|
return ::matchCallArguments(*this, kind, type1, type2, locator);
|
|
}
|
|
|
|
if (flags & TMF_GenerateConstraints) {
|
|
// Add a new constraint between these types. We consider the current
|
|
// type-matching problem to the "solved" by this addition, because
|
|
// this new constraint will be solved at a later point.
|
|
// Obviously, this must not happen at the top level, or the algorithm
|
|
// would not terminate.
|
|
addConstraint(getConstraintKind(kind), type1, type2,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
return SolutionKind::Unsolved;
|
|
}
|
|
|
|
// Decompose parallel structure.
|
|
unsigned subFlags = (flags | TMF_GenerateConstraints) & ~TMF_ApplyingFix;
|
|
if (desugar1->getKind() == desugar2->getKind()) {
|
|
switch (desugar1->getKind()) {
|
|
#define SUGARED_TYPE(id, parent) case TypeKind::id:
|
|
#define TYPE(id, parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
llvm_unreachable("Type has not been desugared completely");
|
|
|
|
#define ARTIFICIAL_TYPE(id, parent) case TypeKind::id:
|
|
#define TYPE(id, parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
llvm_unreachable("artificial type in constraint");
|
|
|
|
#define BUILTIN_TYPE(id, parent) case TypeKind::id:
|
|
#define TYPE(id, parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
case TypeKind::Module:
|
|
if (desugar1 == desugar2) {
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// Record this failure.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
getRelationalFailureKind(kind), type1, type2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
|
|
case TypeKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case TypeKind::GenericTypeParam:
|
|
case TypeKind::DependentMember:
|
|
llvm_unreachable("unmapped dependent type in type checker");
|
|
|
|
case TypeKind::TypeVariable:
|
|
case TypeKind::Archetype:
|
|
// Nothing to do here; handle type variables and archetypes below.
|
|
break;
|
|
|
|
case TypeKind::Tuple: {
|
|
// Try the tuple-to-tuple conversion.
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::TupleToTuple);
|
|
break;
|
|
}
|
|
|
|
case TypeKind::Enum:
|
|
case TypeKind::Struct:
|
|
case TypeKind::Class: {
|
|
auto nominal1 = cast<NominalType>(desugar1);
|
|
auto nominal2 = cast<NominalType>(desugar2);
|
|
if (nominal1->getDecl() == nominal2->getDecl()) {
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TypeKind::DynamicSelf:
|
|
// FIXME: Deep equality? What is the rule between two DynamicSelfs?
|
|
break;
|
|
|
|
case TypeKind::Protocol:
|
|
// Nothing to do here; try existential and user-defined conversions below.
|
|
break;
|
|
|
|
case TypeKind::Metatype:
|
|
case TypeKind::ExistentialMetatype: {
|
|
auto meta1 = cast<AnyMetatypeType>(desugar1);
|
|
auto meta2 = cast<AnyMetatypeType>(desugar2);
|
|
|
|
// metatype<B> < metatype<A> if A < B and both A and B are classes.
|
|
TypeMatchKind subKind = TypeMatchKind::SameType;
|
|
if (isa<MetatypeType>(desugar1) &&
|
|
kind != TypeMatchKind::SameType &&
|
|
(meta1->getInstanceType()->mayHaveSuperclass() ||
|
|
meta2->getInstanceType()->getClassOrBoundGenericClass()))
|
|
subKind = std::min(kind, TypeMatchKind::Subtype);
|
|
|
|
return matchTypes(meta1->getInstanceType(), meta2->getInstanceType(),
|
|
subKind, subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::InstanceType));
|
|
}
|
|
|
|
case TypeKind::Function:
|
|
return matchFunctionTypes(cast<FunctionType>(desugar1),
|
|
cast<FunctionType>(desugar2),
|
|
kind, flags, locator);
|
|
|
|
case TypeKind::PolymorphicFunction:
|
|
case TypeKind::GenericFunction:
|
|
llvm_unreachable("Polymorphic function type should have been opened");
|
|
|
|
case TypeKind::ProtocolComposition:
|
|
// Existential types handled below.
|
|
break;
|
|
|
|
case TypeKind::LValue:
|
|
return matchTypes(cast<LValueType>(desugar1)->getObjectType(),
|
|
cast<LValueType>(desugar2)->getObjectType(),
|
|
TypeMatchKind::SameType, subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::ArrayElementType));
|
|
|
|
case TypeKind::InOut:
|
|
// If the RHS is an inout type, the LHS must be an @lvalue type.
|
|
if (kind >= TypeMatchKind::OperatorArgumentConversion) {
|
|
if (shouldRecordFailures())
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::IsForbiddenLValue, type1, type2);
|
|
return SolutionKind::Error;
|
|
}
|
|
return matchTypes(cast<InOutType>(desugar1)->getObjectType(),
|
|
cast<InOutType>(desugar2)->getObjectType(),
|
|
TypeMatchKind::SameType, subFlags,
|
|
locator.withPathElement(ConstraintLocator::ArrayElementType));
|
|
|
|
case TypeKind::UnboundGeneric:
|
|
llvm_unreachable("Unbound generic type should have been opened");
|
|
|
|
case TypeKind::BoundGenericClass:
|
|
case TypeKind::BoundGenericEnum:
|
|
case TypeKind::BoundGenericStruct: {
|
|
auto bound1 = cast<BoundGenericType>(desugar1);
|
|
auto bound2 = cast<BoundGenericType>(desugar2);
|
|
|
|
if (bound1->getDecl() == bound2->getDecl()) {
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (concrete && kind >= TypeMatchKind::Subtype) {
|
|
auto tuple1 = type1->getAs<TupleType>();
|
|
auto tuple2 = type2->getAs<TupleType>();
|
|
|
|
// Detect when the source and destination are both permit scalar
|
|
// conversions, but the source has a name and the destination does not have
|
|
// the same name.
|
|
bool tuplesWithMismatchedNames = false;
|
|
if (tuple1 && tuple2) {
|
|
int scalar1 = tuple1->getFieldForScalarInit();
|
|
int scalar2 = tuple2->getFieldForScalarInit();
|
|
if (scalar1 >= 0 && scalar2 >= 0) {
|
|
auto name1 = tuple1->getFields()[scalar1].getName();
|
|
auto name2 = tuple2->getFields()[scalar2].getName();
|
|
tuplesWithMismatchedNames = !name1.empty() && name1 != name2;
|
|
}
|
|
}
|
|
|
|
if (tuple2 && !tuplesWithMismatchedNames) {
|
|
// A scalar type is a trivial subtype of a one-element, non-variadic tuple
|
|
// containing a single element if the scalar type is a subtype of
|
|
// the type of that tuple's element.
|
|
//
|
|
// A scalar type can be converted to a tuple so long as there is at
|
|
// most one non-defaulted element.
|
|
if ((tuple2->getFields().size() == 1 &&
|
|
!tuple2->getFields()[0].isVararg()) ||
|
|
(kind >= TypeMatchKind::Conversion &&
|
|
tuple2->getFieldForScalarInit() >= 0)) {
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::ScalarToTuple);
|
|
|
|
// FIXME: Prohibits some user-defined conversions for tuples.
|
|
goto commit_to_conversions;
|
|
}
|
|
}
|
|
|
|
if (tuple1 && !tuplesWithMismatchedNames) {
|
|
// A single-element tuple can be a trivial subtype of a scalar.
|
|
if (tuple1->getFields().size() == 1 &&
|
|
!tuple1->getFields()[0].isVararg()) {
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::TupleToScalar);
|
|
}
|
|
}
|
|
|
|
// Subclass-to-superclass conversion.
|
|
if (type1->mayHaveSuperclass() && type2->mayHaveSuperclass() &&
|
|
type2->getClassOrBoundGenericClass() &&
|
|
type1->getClassOrBoundGenericClass()
|
|
!= type2->getClassOrBoundGenericClass()) {
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
|
|
}
|
|
|
|
// Implicit array conversions.
|
|
if (kind >= TypeMatchKind::Conversion) {
|
|
if (isArrayType(desugar1) && isArrayType(desugar2)) {
|
|
|
|
// Push both the upcast and bridged conversion kinds. We'll construct
|
|
// a disjunctive constraint out of them, favoring the upcast conversion
|
|
// should there be a tie.
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayBridged);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (concrete && kind >= TypeMatchKind::Conversion) {
|
|
// An lvalue of type T1 can be converted to a value of type T2 so long as
|
|
// T1 is convertible to T2 (by loading the value).
|
|
if (type1->is<LValueType>())
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::LValueToRValue);
|
|
|
|
// An expression can be converted to an auto-closure function type, creating
|
|
// an implicit closure.
|
|
if (auto function2 = type2->getAs<FunctionType>()) {
|
|
if (function2->isAutoClosure())
|
|
return matchTypes(type1, function2->getResult(), kind, subFlags,
|
|
locator.withPathElement(ConstraintLocator::Load));
|
|
}
|
|
}
|
|
|
|
if (concrete && kind >= TypeMatchKind::OperatorArgumentConversion) {
|
|
// If the RHS is an inout type, the LHS must be an @lvalue type.
|
|
if (auto *iot = type2->getAs<InOutType>()) {
|
|
return matchTypes(type1, LValueType::get(iot->getObjectType()),
|
|
kind, subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::ArrayElementType));
|
|
}
|
|
}
|
|
|
|
// For a subtyping relation involving a conversion to an existential
|
|
// metatype, match the child types.
|
|
if (concrete && kind >= TypeMatchKind::Subtype) {
|
|
if (auto meta1 = type1->getAs<MetatypeType>()) {
|
|
if (auto meta2 = type2->getAs<ExistentialMetatypeType>()) {
|
|
return matchTypes(meta1->getInstanceType(),
|
|
meta2->getInstanceType(),
|
|
TypeMatchKind::Subtype, subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::InstanceType));
|
|
}
|
|
}
|
|
}
|
|
|
|
// For a subtyping relation involving two existential types or subtyping of
|
|
// a class existential type, or a conversion from any type to an
|
|
// existential type, check whether the first type conforms to each of the
|
|
// protocols in the second type.
|
|
if (concrete && kind >= TypeMatchKind::Subtype &&
|
|
type2->isExistentialType()) {
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::Existential);
|
|
}
|
|
|
|
// A value of type T can be converted to type U? if T is convertible to U.
|
|
// A value of type T? can be converted to type U? if T is convertible to U.
|
|
// The above conversions also apply to implicitly unwrapped optional types, except
|
|
// that there is no implicit conversion from T? to T!.
|
|
{
|
|
BoundGenericType *boundGenericType2;
|
|
|
|
if (concrete && kind >= TypeMatchKind::Subtype &&
|
|
(boundGenericType2 = type2->getAs<BoundGenericType>())) {
|
|
auto decl2 = boundGenericType2->getDecl();
|
|
if (auto optionalKind2 = decl2->classifyAsOptionalType()) {
|
|
assert(boundGenericType2->getGenericArgs().size() == 1);
|
|
|
|
BoundGenericType *boundGenericType1 = type1->getAs<BoundGenericType>();
|
|
if (boundGenericType1) {
|
|
auto decl1 = boundGenericType1->getDecl();
|
|
if (decl1 == decl2) {
|
|
assert(boundGenericType1->getGenericArgs().size() == 1);
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::OptionalToOptional);
|
|
} else if (optionalKind2 == OTK_Optional &&
|
|
decl1 == TC.Context.getImplicitlyUnwrappedOptionalDecl()) {
|
|
assert(boundGenericType1->getGenericArgs().size() == 1);
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::ImplicitlyUnwrappedOptionalToOptional);
|
|
} else if (optionalKind2 == OTK_ImplicitlyUnwrappedOptional &&
|
|
kind >= TypeMatchKind::Conversion &&
|
|
decl1 == TC.Context.getOptionalDecl()) {
|
|
assert(boundGenericType1->getGenericArgs().size() == 1);
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::OptionalToImplicitlyUnwrappedOptional);
|
|
}
|
|
}
|
|
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::ValueToOptional);
|
|
}
|
|
}
|
|
}
|
|
|
|
// A value of type T! can be (unsafely) forced to U if T
|
|
// is convertible to U.
|
|
{
|
|
Type objectType1;
|
|
if (concrete && kind >= TypeMatchKind::Conversion &&
|
|
(objectType1 = lookThroughImplicitlyUnwrappedOptionalType(type1))) {
|
|
conversionsOrFixes.push_back(
|
|
ConversionRestrictionKind::ForceUnchecked);
|
|
}
|
|
}
|
|
|
|
// A nominal type can be converted to another type via a user-defined
|
|
// conversion function.
|
|
if (concrete && kind >= TypeMatchKind::Conversion &&
|
|
!(flags & TMF_ApplyingOperatorParameter) &&
|
|
shouldTryUserConversion(*this, type1)) {
|
|
conversionsOrFixes.push_back(ConversionRestrictionKind::User);
|
|
|
|
// Favor array conversions to non-array types (such as NSArray).
|
|
if (this->isArrayType(desugar1) && !this->isArrayType(desugar2)) {
|
|
this->increaseScore(SK_UserConversion);
|
|
}
|
|
}
|
|
|
|
commit_to_conversions:
|
|
// When we hit this point, we're committed to the set of potential
|
|
// conversions recorded thus far.
|
|
//
|
|
//
|
|
// FIXME: One should only jump to this label in the case where we want to
|
|
// cut off other potential conversions because we know none of them apply.
|
|
// Gradually, those gotos should go away as we can handle more kinds of
|
|
// conversions via disjunction constraints.
|
|
|
|
// If we should attempt fixes, add those to the list. They'll only be visited
|
|
// if there are no other possible solutions.
|
|
if (shouldAttemptFixes() && !typeVar1 && !typeVar2 &&
|
|
!(flags & TMF_ApplyingFix) && kind >= TypeMatchKind::Conversion) {
|
|
Type objectType1 = type1->getRValueObjectType();
|
|
|
|
// If the source type is a function type that could be applied with (),
|
|
// try it.
|
|
if (isFunctionTypeAcceptingNoArguments(objectType1)) {
|
|
conversionsOrFixes.push_back(FixKind::NullaryCall);
|
|
}
|
|
|
|
// If we have an optional type, try to force-unwrap it.
|
|
// FIXME: Should we also try '?'?
|
|
if (objectType1->getOptionalObjectType()) {
|
|
conversionsOrFixes.push_back(FixKind::ForceOptional);
|
|
}
|
|
|
|
// If we have a value of type AnyObject that we're trying to convert to
|
|
// a class, force a downcast.
|
|
// FIXME: Also allow types bridged through Objective-C classes.
|
|
if (objectType1->isAnyObject() &&
|
|
type2->getClassOrBoundGenericClass()) {
|
|
conversionsOrFixes.push_back(Fix::getForcedDowncast(*this, type2));
|
|
}
|
|
|
|
// If we're converting an lvalue to an inout type, add the missing '&'.
|
|
if (type2->getRValueType()->is<InOutType>() && type1->is<LValueType>()) {
|
|
conversionsOrFixes.push_back(FixKind::AddressOf);
|
|
}
|
|
}
|
|
|
|
if (conversionsOrFixes.empty()) {
|
|
// If one of the types is a type variable, we leave this unsolved.
|
|
if (typeVar1 || typeVar2)
|
|
return SolutionKind::Unsolved;
|
|
|
|
// If we are supposed to record failures, do so.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
getRelationalFailureKind(kind), type1, type2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Where there is more than one potential conversion, create a disjunction
|
|
// so that we'll explore all of the options.
|
|
if (conversionsOrFixes.size() > 1) {
|
|
auto fixedLocator = getConstraintLocator(locator);
|
|
SmallVector<Constraint *, 2> constraints;
|
|
for (auto potential : conversionsOrFixes) {
|
|
auto constraintKind = getConstraintKind(kind);
|
|
|
|
if (auto restriction = potential.getRestriction()) {
|
|
// Determine the constraint kind. For a deep equality constraint, only
|
|
// perform equality.
|
|
if (*restriction == ConversionRestrictionKind::DeepEquality)
|
|
constraintKind = ConstraintKind::Equal;
|
|
|
|
constraints.push_back(
|
|
Constraint::createRestricted(*this, constraintKind, *restriction,
|
|
type1, type2, fixedLocator));
|
|
continue;
|
|
}
|
|
|
|
// If the first thing we found is a fix, add a "don't fix" marker.
|
|
if (conversionsOrFixes.empty()) {
|
|
constraints.push_back(
|
|
Constraint::createFixed(*this, constraintKind, FixKind::None,
|
|
type1, type2, fixedLocator));
|
|
}
|
|
|
|
auto fix = *potential.getFix();
|
|
constraints.push_back(
|
|
Constraint::createFixed(*this, constraintKind, fix, type1, type2,
|
|
fixedLocator));
|
|
}
|
|
addConstraint(Constraint::createDisjunction(*this, constraints,
|
|
fixedLocator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// For a single potential conversion, directly recurse, so that we
|
|
// don't allocate a new constraint or constraint locator.
|
|
|
|
// Handle restrictions.
|
|
if (auto restriction = conversionsOrFixes[0].getRestriction()) {
|
|
ConstraintRestrictions.push_back({type1, type2, *restriction});
|
|
|
|
if (flags & TMF_UnwrappingOptional) {
|
|
subFlags |= TMF_UnwrappingOptional;
|
|
}
|
|
|
|
return simplifyRestrictedConstraint(*restriction, type1, type2,
|
|
kind, subFlags, locator);
|
|
}
|
|
|
|
// Handle fixes.
|
|
auto fix = *conversionsOrFixes[0].getFix();
|
|
return simplifyFixConstraint(fix, type1, type2, kind, subFlags, locator);
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyConstructionConstraint(Type valueType, Type argType,
|
|
unsigned flags,
|
|
ConstraintLocator *locator) {
|
|
// Desugar the value type.
|
|
auto desugarValueType = valueType->getDesugaredType();
|
|
|
|
// If we have a type variable that has been bound to a fixed type,
|
|
// look through to that fixed type.
|
|
auto desugarValueTypeVar = dyn_cast<TypeVariableType>(desugarValueType);
|
|
if (desugarValueTypeVar) {
|
|
if (auto fixed = getFixedType(desugarValueTypeVar)) {
|
|
valueType = fixed;
|
|
desugarValueType = fixed->getDesugaredType();
|
|
desugarValueTypeVar = nullptr;
|
|
}
|
|
}
|
|
|
|
switch (desugarValueType->getKind()) {
|
|
#define SUGARED_TYPE(id, parent) case TypeKind::id:
|
|
#define TYPE(id, parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
llvm_unreachable("Type has not been desugared completely");
|
|
|
|
#define ARTIFICIAL_TYPE(id, parent) case TypeKind::id:
|
|
#define TYPE(id, parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
llvm_unreachable("artificial type in constraint");
|
|
|
|
case TypeKind::Error:
|
|
return SolutionKind::Error;
|
|
|
|
case TypeKind::GenericFunction:
|
|
case TypeKind::GenericTypeParam:
|
|
case TypeKind::DependentMember:
|
|
llvm_unreachable("unmapped dependent type");
|
|
|
|
case TypeKind::TypeVariable:
|
|
return SolutionKind::Unsolved;
|
|
|
|
case TypeKind::Tuple: {
|
|
// Tuple construction is simply tuple conversion.
|
|
return matchTypes(argType, valueType, TypeMatchKind::Conversion,
|
|
flags|TMF_GenerateConstraints, locator);
|
|
}
|
|
|
|
case TypeKind::Enum:
|
|
case TypeKind::Struct:
|
|
case TypeKind::Class:
|
|
case TypeKind::BoundGenericClass:
|
|
case TypeKind::BoundGenericEnum:
|
|
case TypeKind::BoundGenericStruct:
|
|
case TypeKind::Archetype:
|
|
case TypeKind::DynamicSelf:
|
|
case TypeKind::ProtocolComposition:
|
|
case TypeKind::Protocol:
|
|
// Break out to handle the actual construction below.
|
|
break;
|
|
|
|
case TypeKind::PolymorphicFunction:
|
|
llvm_unreachable("Polymorphic function type should have been opened");
|
|
|
|
case TypeKind::UnboundGeneric:
|
|
llvm_unreachable("Unbound generic type should have been opened");
|
|
|
|
#define BUILTIN_TYPE(id, parent) case TypeKind::id:
|
|
#define TYPE(id, parent)
|
|
#include "swift/AST/TypeNodes.def"
|
|
case TypeKind::ExistentialMetatype:
|
|
case TypeKind::Metatype:
|
|
case TypeKind::Function:
|
|
case TypeKind::LValue:
|
|
case TypeKind::InOut:
|
|
case TypeKind::Module:
|
|
// If we are supposed to record failures, do so.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(locator, Failure::TypesNotConstructible,
|
|
valueType, argType);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
auto ctors = TC.lookupConstructors(valueType, DC);
|
|
if (!ctors) {
|
|
// If we are supposed to record failures, do so.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(locator, Failure::TypesNotConstructible,
|
|
valueType, argType);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
auto &context = getASTContext();
|
|
auto name = context.Id_init;
|
|
auto applyLocator = getConstraintLocator(locator,
|
|
ConstraintLocator::ApplyArgument);
|
|
auto tv = createTypeVariable(applyLocator,
|
|
TVO_CanBindToLValue|TVO_PrefersSubtypeBinding);
|
|
|
|
// The constructor will have function type T -> T2, for a fresh type
|
|
// variable T. Note that these constraints specifically require a
|
|
// match on the result type because the constructors for enums and struct
|
|
// types always return a value of exactly that type.
|
|
addValueMemberConstraint(valueType, name,
|
|
FunctionType::get(tv, valueType),
|
|
getConstraintLocator(
|
|
locator,
|
|
ConstraintLocator::ConstructorMember));
|
|
|
|
// The first type must be convertible to the constructor's argument type.
|
|
addConstraint(ConstraintKind::ArgumentTupleConversion, argType, tv,
|
|
applyLocator);
|
|
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
|
|
Type type,
|
|
ProtocolDecl *protocol,
|
|
ConstraintLocatorBuilder locator,
|
|
unsigned flags,
|
|
bool allowNonConformingExistential) {
|
|
// Dig out the fixed type to which this type refers.
|
|
TypeVariableType *typeVar;
|
|
type = getFixedTypeRecursive(type, typeVar, /*wantRValue=*/true);
|
|
|
|
// If we hit a type variable without a fixed type, we can't
|
|
// solve this yet.
|
|
if (typeVar)
|
|
return SolutionKind::Unsolved;
|
|
|
|
// If existential types don't need to conform (i.e., they only need to
|
|
// contain the protocol), check that separately.
|
|
if (allowNonConformingExistential && type->isExistentialType()) {
|
|
SmallVector<ProtocolDecl *, 4> protocols;
|
|
type->getAnyExistentialTypeProtocols(protocols);
|
|
|
|
for (auto ap : protocols) {
|
|
// If this isn't the protocol we're looking for, continue looking.
|
|
if (ap == protocol || ap->inheritsFrom(protocol))
|
|
return SolutionKind::Solved;
|
|
}
|
|
} else {
|
|
// Check whether this type conforms to the protocol.
|
|
if (TC.conformsToProtocol(type, protocol, DC))
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// There's nothing more we can do; fail.
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::DoesNotConformToProtocol, type,
|
|
protocol->getDeclaredType());
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
/// Determine the kind of checked cast to perform from the given type to
|
|
/// the given type.
|
|
///
|
|
/// This routine does not attempt to check whether the cast can actually
|
|
/// succeed; that's the caller's responsibility.
|
|
static CheckedCastKind getCheckedCastKind(ConstraintSystem *cs,
|
|
Type fromType,
|
|
Type toType) {
|
|
// Peel off optionals metatypes from the types, because we might cast through
|
|
// them.
|
|
toType = toType->lookThroughAllAnyOptionalTypes();
|
|
fromType = fromType->lookThroughAllAnyOptionalTypes();
|
|
|
|
// Peel off metatypes, since if we can cast two types, we can cast their
|
|
// metatypes.
|
|
while (auto toMetatype = toType->getAs<MetatypeType>()) {
|
|
auto fromMetatype = fromType->getAs<MetatypeType>();
|
|
if (!fromMetatype)
|
|
break;
|
|
toType = toMetatype->getInstanceType();
|
|
fromType = fromMetatype->getInstanceType();
|
|
}
|
|
|
|
// Peel off a potential layer of existential<->concrete metatype conversion.
|
|
if (auto toMetatype = toType->getAs<AnyMetatypeType>()) {
|
|
if (auto fromMetatype = fromType->getAs<MetatypeType>()) {
|
|
toType = toMetatype->getInstanceType();
|
|
fromType = fromMetatype->getInstanceType();
|
|
}
|
|
}
|
|
|
|
// Classify the from/to types.
|
|
bool toArchetype = toType->is<ArchetypeType>();
|
|
bool fromArchetype = fromType->is<ArchetypeType>();
|
|
bool toExistential = toType->isExistentialType();
|
|
bool fromExistential = fromType->isExistentialType();
|
|
|
|
// Array downcasts are handled specially.
|
|
if (cs->isArrayType(fromType) && cs->isArrayType(toType)) {
|
|
return CheckedCastKind::ArrayDowncast;
|
|
}
|
|
|
|
// We can only downcast to an existential if the destination protocols are
|
|
// objc and the source type is an objc class or an existential bounded by objc
|
|
// protocols.
|
|
if (toExistential) {
|
|
return CheckedCastKind::ConcreteToUnrelatedExistential;
|
|
}
|
|
|
|
// A downcast can:
|
|
// - convert an archetype to a (different) archetype type.
|
|
if (fromArchetype && toArchetype) {
|
|
return CheckedCastKind::ArchetypeToArchetype;
|
|
}
|
|
|
|
// - convert from an existential to an archetype or conforming concrete
|
|
// type.
|
|
if (fromExistential) {
|
|
if (toArchetype) {
|
|
return CheckedCastKind::ExistentialToArchetype;
|
|
}
|
|
|
|
return CheckedCastKind::ExistentialToConcrete;
|
|
}
|
|
|
|
// - convert an archetype to a concrete type fulfilling its constraints.
|
|
if (fromArchetype) {
|
|
return CheckedCastKind::ArchetypeToConcrete;
|
|
}
|
|
|
|
if (toArchetype) {
|
|
// - convert from a superclass to an archetype.
|
|
if (toType->castTo<ArchetypeType>()->getSuperclass()) {
|
|
return CheckedCastKind::SuperToArchetype;
|
|
}
|
|
|
|
// - convert a concrete type to an archetype for which it fulfills
|
|
// constraints.
|
|
return CheckedCastKind::ConcreteToArchetype;
|
|
}
|
|
|
|
// The remaining case is a class downcast.
|
|
assert(!fromArchetype && "archetypes should have been handled above");
|
|
assert(!toArchetype && "archetypes should have been handled above");
|
|
assert(!fromExistential && "existentials should have been handled above");
|
|
assert(!toExistential && "existentials should have been handled above");
|
|
|
|
return CheckedCastKind::Downcast;
|
|
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyCheckedCastConstraint(
|
|
Type fromType, Type toType,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Dig out the fixed type to which this type refers.
|
|
TypeVariableType *typeVar1;
|
|
fromType = getFixedTypeRecursive(fromType, typeVar1, /*wantRValue=*/true);
|
|
|
|
// If we hit a type variable without a fixed type, we can't
|
|
// solve this yet.
|
|
if (typeVar1)
|
|
return SolutionKind::Unsolved;
|
|
|
|
// Dig out the fixed type to which this type refers.
|
|
TypeVariableType *typeVar2;
|
|
toType = getFixedTypeRecursive(toType, typeVar2, /*wantRValue=*/true);
|
|
|
|
// If we hit a type variable without a fixed type, we can't
|
|
// solve this yet.
|
|
if (typeVar2)
|
|
return SolutionKind::Unsolved;
|
|
|
|
auto kind = getCheckedCastKind(this, fromType, toType);
|
|
switch (kind) {
|
|
case CheckedCastKind::ArrayDowncast: {
|
|
auto fromBaseType = getBaseTypeForArrayType(fromType.getPointer());
|
|
auto toBaseType = getBaseTypeForArrayType(toType.getPointer());
|
|
|
|
// FIXME: Deal with from/to base types that haven't been solved
|
|
// down to type variables yet.
|
|
|
|
// Check whether we need to bridge through an Objective-C class.
|
|
if (auto classType = TC.getDynamicBridgedThroughObjCClass(DC, fromBaseType,
|
|
toBaseType)) {
|
|
// The class we're bridging through must be a subtype of the type we're
|
|
// coming from.
|
|
addConstraint(ConstraintKind::Subtype, classType, fromBaseType,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
addConstraint(ConstraintKind::Subtype, toBaseType, fromBaseType,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
case CheckedCastKind::ArchetypeToArchetype:
|
|
case CheckedCastKind::ConcreteToUnrelatedExistential:
|
|
case CheckedCastKind::ExistentialToArchetype:
|
|
case CheckedCastKind::SuperToArchetype:
|
|
return SolutionKind::Solved;
|
|
|
|
case CheckedCastKind::ArchetypeToConcrete:
|
|
case CheckedCastKind::ConcreteToArchetype:
|
|
// FIXME: Check substitutability.
|
|
return SolutionKind::Solved;
|
|
|
|
case CheckedCastKind::Downcast:
|
|
case CheckedCastKind::ExistentialToConcrete: {
|
|
// Peel off optionals metatypes from the types, because we might cast through
|
|
// them.
|
|
auto toValueType = toType->lookThroughAllAnyOptionalTypes();
|
|
auto fromValueType = fromType->lookThroughAllAnyOptionalTypes();
|
|
|
|
// This existential-to-concrete cast might bridge through an Objective-C
|
|
// class type.
|
|
if (auto classType = TC.getDynamicBridgedThroughObjCClass(DC, fromValueType,
|
|
toValueType)) {
|
|
// The class we're bridging through must be a subtype of the type we're
|
|
// coming from.
|
|
addConstraint(ConstraintKind::Subtype, classType, fromValueType,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
addConstraint(ConstraintKind::Subtype, toType, fromType,
|
|
getConstraintLocator(locator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
case CheckedCastKind::Coercion:
|
|
case CheckedCastKind::Unresolved:
|
|
llvm_unreachable("Not a valid result");
|
|
}
|
|
}
|
|
|
|
/// Determine whether the given type is the Self type of the protocol.
|
|
static bool isProtocolSelf(Type type) {
|
|
if (auto genericParam = type->getAs<GenericTypeParamType>())
|
|
return genericParam->getDepth() == 0;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Determine whether the given type contains a reference to the 'Self' type
|
|
/// of a protocol.
|
|
static bool containsProtocolSelf(Type type) {
|
|
// If the type is not dependent, it doesn't refer to 'Self'.
|
|
if (!type->isDependentType())
|
|
return false;
|
|
|
|
return type.findIf([](Type type) -> bool {
|
|
return isProtocolSelf(type);
|
|
});
|
|
}
|
|
|
|
/// Determine whether the given protocol member's signature prevents
|
|
/// it from being used in an existential reference.
|
|
static bool isUnavailableInExistential(TypeChecker &tc, ValueDecl *decl) {
|
|
Type type = decl->getInterfaceType();
|
|
|
|
// For a function or constructor, skip the implicit 'this'.
|
|
if (auto afd = dyn_cast<AbstractFunctionDecl>(decl)) {
|
|
type = type->castTo<AnyFunctionType>()->getResult();
|
|
|
|
// Allow functions to return Self, but not have Self anywhere in
|
|
// their argument types.
|
|
for (unsigned i = 1, n = afd->getNumParamPatterns(); i != n; ++i) {
|
|
// Check whether the input type contains Self anywhere.
|
|
auto fnType = type->castTo<AnyFunctionType>();
|
|
if (containsProtocolSelf(fnType->getInput()))
|
|
return true;
|
|
|
|
type = fnType->getResult();
|
|
}
|
|
|
|
// Look through one level of optional on the result type.
|
|
if (auto valueType = type->getAnyOptionalObjectType())
|
|
type = valueType;
|
|
|
|
if (isProtocolSelf(type) || type->is<DynamicSelfType>())
|
|
return false;
|
|
|
|
return containsProtocolSelf(type);
|
|
}
|
|
|
|
return containsProtocolSelf(type);
|
|
}
|
|
|
|
static DeclName
|
|
getNameForValueLookup(
|
|
ASTContext &ctx,
|
|
const Constraint &constraint,
|
|
Type baseObjTy,
|
|
const llvm::DenseMap<UnresolvedDotExpr *, ApplyExpr *> &callMap) {
|
|
auto origName = constraint.getMember();
|
|
if (origName.isCompoundName())
|
|
return origName;
|
|
|
|
auto protoTy = baseObjTy->getAs<ProtocolType>();
|
|
if (!protoTy ||
|
|
!protoTy->getDecl()->isSpecificProtocol(KnownProtocolKind::AnyObject))
|
|
return origName;
|
|
|
|
// FIXME: Pulling out the expr from the locator is pretty hacky.
|
|
const ConstraintLocator *loc = constraint.getLocator();
|
|
if (!loc)
|
|
return origName;
|
|
|
|
auto UDE = dyn_cast_or_null<UnresolvedDotExpr>(loc->getAnchor());
|
|
if (!UDE)
|
|
return origName;
|
|
|
|
const ApplyExpr *call = callMap.lookup(UDE);
|
|
if (!call)
|
|
return origName;
|
|
|
|
// Handle the one-argument, no-argument-name case.
|
|
if (isa<ParenExpr>(call->getArg()))
|
|
return DeclName(ctx, origName.getBaseName(), Identifier());
|
|
|
|
auto args = dyn_cast<TupleExpr>(call->getArg());
|
|
if (!args)
|
|
return origName;
|
|
|
|
return DeclName(ctx, origName.getBaseName(), args->getElementNames());
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyMemberConstraint(const Constraint &constraint) {
|
|
// Resolve the base type, if we can. If we can't resolve the base type,
|
|
// then we can't solve this constraint.
|
|
Type baseTy = simplifyType(constraint.getFirstType());
|
|
Type baseObjTy = baseTy->getRValueType();
|
|
|
|
// Try to look through ImplicitlyUnwrappedOptional<T>; the result is always an r-value.
|
|
if (auto objTy = lookThroughImplicitlyUnwrappedOptionalType(baseObjTy)) {
|
|
increaseScore(SK_ForceUnchecked);
|
|
baseTy = baseObjTy = objTy;
|
|
}
|
|
|
|
// Dig out the instance type.
|
|
bool isMetatype = false;
|
|
Type instanceTy = baseObjTy;
|
|
if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
|
|
instanceTy = baseObjMeta->getInstanceType();
|
|
isMetatype = true;
|
|
}
|
|
|
|
if (instanceTy->is<TypeVariableType>())
|
|
return SolutionKind::Unsolved;
|
|
|
|
// If the base type is a tuple type, look for the named or indexed member
|
|
// of the tuple.
|
|
DeclName name = constraint.getMember();
|
|
Type memberTy = constraint.getSecondType();
|
|
if (auto baseTuple = baseObjTy->getAs<TupleType>()) {
|
|
// Tuples don't have compound-name members.
|
|
if (!name.isSimpleName()) {
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
StringRef nameStr = name.getBaseName().str();
|
|
int fieldIdx = -1;
|
|
// Resolve a number reference into the tuple type.
|
|
unsigned Value = 0;
|
|
if (!nameStr.getAsInteger(10, Value) &&
|
|
Value < baseTuple->getFields().size()) {
|
|
fieldIdx = Value;
|
|
} else {
|
|
fieldIdx = baseTuple->getNamedElementId(name.getBaseName());
|
|
}
|
|
|
|
if (fieldIdx == -1) {
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Add an overload set that selects this field.
|
|
OverloadChoice choice(baseTy, fieldIdx);
|
|
addBindOverloadConstraint(memberTy, choice, constraint.getLocator());
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// FIXME: If the base type still involves type variables, we want this
|
|
// constraint to be unsolved. This effectively requires us to solve the
|
|
// left-hand side of a dot expression before we look for members.
|
|
|
|
bool isExistential = instanceTy->isExistentialType();
|
|
if (name.isSimpleName(TC.Context.Id_init)) {
|
|
// Constructors have their own approach to name lookup.
|
|
auto ctors = TC.lookupConstructors(baseObjTy, DC);
|
|
if (!ctors) {
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Introduce a new overload set.
|
|
SmallVector<OverloadChoice, 4> choices;
|
|
for (auto constructor : ctors) {
|
|
// If the constructor is invalid, skip it.
|
|
// FIXME: Note this as invalid, in case we don't find a solution,
|
|
// so we don't let errors cascade further.
|
|
TC.validateDecl(constructor, true);
|
|
if (constructor->isInvalid())
|
|
continue;
|
|
|
|
// If our base is an existential type, we can't make use of any
|
|
// constructor whose signature involves associated types.
|
|
// FIXME: Mark this as 'unavailable'.
|
|
if (isExistential &&
|
|
isUnavailableInExistential(getTypeChecker(), constructor))
|
|
continue;
|
|
|
|
choices.push_back(OverloadChoice(baseTy, constructor,
|
|
/*isSpecialized=*/false));
|
|
}
|
|
|
|
if (choices.empty()) {
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
addOverloadSet(memberTy, choices, constraint.getLocator());
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// If we want member types only, use member type lookup.
|
|
if (constraint.getKind() == ConstraintKind::TypeMember) {
|
|
// Types don't have compound names.
|
|
if (!name.isSimpleName()) {
|
|
// FIXME: Customize diagnostic to mention types and compound names.
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
auto lookup = TC.lookupMemberType(baseObjTy, name.getBaseName(), DC);
|
|
if (!lookup) {
|
|
// FIXME: Customize diagnostic to mention types.
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Form the overload set.
|
|
SmallVector<OverloadChoice, 4> choices;
|
|
for (auto result : lookup) {
|
|
// If the result is invalid, skip it.
|
|
// FIXME: Note this as invalid, in case we don't find a solution,
|
|
// so we don't let errors cascade further.
|
|
TC.validateDecl(result.first, true);
|
|
if (result.first->isInvalid())
|
|
continue;
|
|
|
|
choices.push_back(OverloadChoice(baseTy, result.first,
|
|
/*isSpecialized=*/false));
|
|
}
|
|
|
|
if (choices.empty()) {
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
auto locator = getConstraintLocator(constraint.getLocator());
|
|
addOverloadSet(memberTy, choices, locator);
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// If we're doing dynamic lookup, and this is a call, use the full name of
|
|
// the member.
|
|
name = getNameForValueLookup(getASTContext(), constraint, baseObjTy,
|
|
PossibleDynamicLookupCalls);
|
|
|
|
// Look for members within the base.
|
|
LookupResult &lookup = lookupMember(baseObjTy, name);
|
|
if (!lookup) {
|
|
// Check whether we actually performed a lookup with an integer value.
|
|
unsigned index;
|
|
if (name.isSimpleName()
|
|
&& !name.getBaseName().str().getAsInteger(10, index)) {
|
|
// ".0" on a scalar just refers to the underlying scalar value.
|
|
if (index == 0) {
|
|
OverloadChoice identityChoice(baseTy, OverloadChoiceKind::BaseType);
|
|
addBindOverloadConstraint(memberTy, identityChoice,
|
|
constraint.getLocator());
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// FIXME: Specialize diagnostic here?
|
|
}
|
|
|
|
recordFailure(constraint.getLocator(), Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// The set of directly accessible types, which is only used when
|
|
// we're performing dynamic lookup into an existential type.
|
|
bool isDynamicLookup = false;
|
|
if (auto protoTy = instanceTy->getAs<ProtocolType>()) {
|
|
isDynamicLookup = protoTy->getDecl()->isSpecificProtocol(
|
|
KnownProtocolKind::AnyObject);
|
|
}
|
|
|
|
// Record the fact that we found a mutating function for diagnostics.
|
|
bool FoundMutating = false;
|
|
|
|
// Introduce a new overload set to capture the choices.
|
|
SmallVector<OverloadChoice, 4> choices;
|
|
for (auto result : lookup) {
|
|
// If the result is invalid, skip it.
|
|
// FIXME: Note this as invalid, in case we don't find a solution,
|
|
// so we don't let errors cascade further.
|
|
TC.validateDecl(result, true);
|
|
if (result->isInvalid())
|
|
continue;
|
|
|
|
// If our base is an existential type, we can't make use of any
|
|
// member whose signature involves associated types.
|
|
// FIXME: Mark this as 'unavailable'.
|
|
if (isExistential && isUnavailableInExistential(getTypeChecker(), result))
|
|
continue;
|
|
|
|
// If we are looking for a metatype member, don't include members that can
|
|
// only be accessed on an instance of the object.
|
|
// FIXME: Mark as 'unavailable' somehow.
|
|
if (isMetatype && !(isa<FuncDecl>(result) || !result->isInstanceMember())) {
|
|
continue;
|
|
}
|
|
|
|
// If we aren't looking in a metatype, ignore static functions, static
|
|
// variables, and enum elements.
|
|
if (!isMetatype && !baseObjTy->is<ModuleType>() &&
|
|
!result->isInstanceMember())
|
|
continue;
|
|
|
|
// If we're doing dynamic lookup into a metatype of AnyObject and we've
|
|
// found an instance member, ignore it.
|
|
if (isDynamicLookup && isMetatype && result->isInstanceMember()) {
|
|
// FIXME: Mark as 'unavailable' somehow.
|
|
continue;
|
|
}
|
|
|
|
// If we have an rvalue base of struct or enum type, make sure that the
|
|
// result isn't mutating (only valid on lvalues).
|
|
if (!isMetatype && !baseObjTy->hasReferenceSemantics() &&
|
|
!baseTy->is<LValueType>() && result->isInstanceMember()) {
|
|
if (auto *FD = dyn_cast<FuncDecl>(result))
|
|
if (FD->isMutating()) {
|
|
FoundMutating = true;
|
|
continue;
|
|
}
|
|
|
|
// Subscripts are ok on rvalues so long as the getter is nonmutating.
|
|
if (auto *SD = dyn_cast<SubscriptDecl>(result))
|
|
if (SD->getGetter()->isMutating()) {
|
|
FoundMutating = true;
|
|
continue;
|
|
}
|
|
|
|
// Computed properties are ok on rvalues so long as the getter is
|
|
// nonmutating.
|
|
if (auto *VD = dyn_cast<VarDecl>(result))
|
|
if (auto *GD = VD->getGetter())
|
|
if (GD->isMutating()) {
|
|
FoundMutating = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If the result's type contains delayed members, we need to force them now.
|
|
if (auto NT = dyn_cast<NominalType>(result->getType().getPointer())) {
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(NT->getDecl())) {
|
|
TC.forceExternalDeclMembers(NTD);
|
|
}
|
|
}
|
|
|
|
// If we're looking into an existential type, check whether this
|
|
// result was found via dynamic lookup.
|
|
if (isDynamicLookup) {
|
|
assert(result->getDeclContext()->isTypeContext() && "Dynamic lookup bug");
|
|
|
|
// We found this declaration via dynamic lookup, record it as such.
|
|
choices.push_back(OverloadChoice::getDeclViaDynamic(baseTy, result));
|
|
continue;
|
|
}
|
|
|
|
choices.push_back(OverloadChoice(baseTy, result, /*isSpecialized=*/false));
|
|
}
|
|
|
|
if (choices.empty()) {
|
|
recordFailure(constraint.getLocator(),
|
|
FoundMutating ? Failure::DoesNotHaveNonMutatingMember :
|
|
Failure::DoesNotHaveMember,
|
|
baseObjTy, name);
|
|
return SolutionKind::Error;
|
|
}
|
|
auto locator = getConstraintLocator(constraint.getLocator());
|
|
addOverloadSet(memberTy, choices, locator);
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyArchetypeConstraint(const Constraint &constraint) {
|
|
// Resolve the base type, if we can. If we can't resolve the base type,
|
|
// then we can't solve this constraint.
|
|
Type baseTy = constraint.getFirstType()->getRValueType();
|
|
if (auto tv = dyn_cast<TypeVariableType>(baseTy.getPointer())) {
|
|
auto fixed = getFixedType(tv);
|
|
if (!fixed)
|
|
return SolutionKind::Unsolved;
|
|
|
|
// Continue with the fixed type.
|
|
baseTy = fixed->getRValueType();
|
|
}
|
|
|
|
if (baseTy->is<ArchetypeType>()) {
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// Record this failure.
|
|
recordFailure(constraint.getLocator(), Failure::IsNotArchetype, baseTy);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
/// Simplify the given type for use in a type property constraint.
|
|
static Type simplifyForTypePropertyConstraint(ConstraintSystem &cs, Type type) {
|
|
if (auto tv = type->getAs<TypeVariableType>()) {
|
|
auto fixed = cs.getFixedType(tv);
|
|
if (!fixed)
|
|
return Type();
|
|
|
|
// Continue with the fixed type.
|
|
type = fixed;
|
|
|
|
// Look through parentheses.
|
|
while (auto paren = type->getAs<ParenType>())
|
|
type = paren->getUnderlyingType();
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyClassConstraint(const Constraint &constraint){
|
|
auto baseTy = simplifyForTypePropertyConstraint(*this,
|
|
constraint.getFirstType());
|
|
if (!baseTy)
|
|
return SolutionKind::Unsolved;
|
|
|
|
if (baseTy->getClassOrBoundGenericClass())
|
|
return SolutionKind::Solved;
|
|
|
|
if (auto archetype = baseTy->getAs<ArchetypeType>()) {
|
|
if (archetype->requiresClass())
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// Record this failure.
|
|
recordFailure(constraint.getLocator(), Failure::IsNotClass, baseTy);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyBridgedToObjectiveCConstraint(
|
|
const Constraint &constraint)
|
|
{
|
|
auto baseTy = simplifyForTypePropertyConstraint(*this,
|
|
constraint.getFirstType());
|
|
if (!baseTy)
|
|
return SolutionKind::Unsolved;
|
|
|
|
if (TC.getBridgedToObjC(DC, baseTy).first) {
|
|
increaseScore(SK_UserConversion);
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// Record this failure.
|
|
recordFailure(constraint.getLocator(),
|
|
Failure::IsNotBridgedToObjectiveC,
|
|
baseTy);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyDynamicTypeOfConstraint(const Constraint &constraint) {
|
|
// Solve forward.
|
|
TypeVariableType *typeVar2;
|
|
Type type2 = getFixedTypeRecursive(constraint.getSecondType(), typeVar2,
|
|
/*wantRValue=*/ true);
|
|
if (!typeVar2) {
|
|
Type dynamicType2;
|
|
if (type2->isAnyExistentialType()) {
|
|
dynamicType2 = ExistentialMetatypeType::get(type2);
|
|
} else {
|
|
dynamicType2 = MetatypeType::get(type2);
|
|
}
|
|
return matchTypes(constraint.getFirstType(), dynamicType2,
|
|
TypeMatchKind::BindType, TMF_GenerateConstraints,
|
|
constraint.getLocator());
|
|
}
|
|
|
|
// Okay, can't solve forward. See what we can do backwards.
|
|
TypeVariableType *typeVar1;
|
|
Type type1 = getFixedTypeRecursive(constraint.getFirstType(), typeVar1, true);
|
|
|
|
// If we have an existential metatype, that's good enough to solve
|
|
// the constraint.
|
|
if (auto metatype1 = type1->getAs<ExistentialMetatypeType>())
|
|
return matchTypes(metatype1->getInstanceType(), type2,
|
|
TypeMatchKind::BindType,
|
|
TMF_GenerateConstraints, constraint.getLocator());
|
|
|
|
// If we have a normal metatype, we can't solve backwards unless we
|
|
// know what kind of object it is.
|
|
if (auto metatype1 = type1->getAs<MetatypeType>()) {
|
|
TypeVariableType *instanceTypeVar1;
|
|
Type instanceType1 = getFixedTypeRecursive(metatype1->getInstanceType(),
|
|
instanceTypeVar1, true);
|
|
if (!instanceTypeVar1) {
|
|
return matchTypes(instanceType1, type2,
|
|
TypeMatchKind::BindType,
|
|
TMF_GenerateConstraints, constraint.getLocator());
|
|
}
|
|
|
|
// If it's definitely not either kind of metatype, then we can
|
|
// report failure right away.
|
|
} else if (!typeVar1) {
|
|
recordFailure(constraint.getLocator(), Failure::IsNotMetatype, type1);
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
return SolutionKind::Unsolved;
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyApplicableFnConstraint(const Constraint &constraint) {
|
|
|
|
// By construction, the left hand side is a type that looks like the
|
|
// following: $T1 -> $T2.
|
|
Type type1 = constraint.getFirstType();
|
|
assert(type1->is<FunctionType>());
|
|
|
|
// Drill down to the concrete type on the right hand side.
|
|
TypeVariableType *typeVar2;
|
|
Type type2 = getFixedTypeRecursive(constraint.getSecondType(), typeVar2,
|
|
/*wantRValue=*/true);
|
|
auto desugar2 = type2->getDesugaredType();
|
|
|
|
// Try to look through ImplicitlyUnwrappedOptional<T>; the result is always an r-value.
|
|
if (auto objTy = lookThroughImplicitlyUnwrappedOptionalType(desugar2)) {
|
|
type2 = objTy;
|
|
desugar2 = type2->getDesugaredType();
|
|
}
|
|
|
|
// Force the right-hand side to be an rvalue.
|
|
unsigned flags = TMF_GenerateConstraints;
|
|
|
|
// If the types are obviously equivalent, we're done.
|
|
if (type1.getPointer() == desugar2)
|
|
return SolutionKind::Solved;
|
|
|
|
// If right-hand side is a type variable, the constraint is unsolved.
|
|
if (typeVar2)
|
|
return SolutionKind::Unsolved;
|
|
|
|
// Strip the 'ApplyFunction' off the locator.
|
|
// FIXME: Perhaps ApplyFunction can go away entirely?
|
|
ConstraintLocatorBuilder locator = constraint.getLocator();
|
|
SmallVector<LocatorPathElt, 2> parts;
|
|
Expr *anchor = locator.getLocatorParts(parts);
|
|
assert(!parts.empty() && "Nonsensical applicable-function locator");
|
|
assert(parts.back().getKind() == ConstraintLocator::ApplyFunction);
|
|
assert(parts.back().getNewSummaryFlags() == 0);
|
|
parts.pop_back();
|
|
ConstraintLocatorBuilder outerLocator =
|
|
getConstraintLocator(anchor, parts, locator.getSummaryFlags());
|
|
|
|
retry:
|
|
// For a function, bind the output and convert the argument to the input.
|
|
auto func1 = type1->castTo<FunctionType>();
|
|
if (desugar2->getKind() == TypeKind::Function) {
|
|
auto func2 = cast<FunctionType>(desugar2);
|
|
|
|
assert(func1->getResult()->is<TypeVariableType>() &&
|
|
"the output of funct1 is a free variable by construction");
|
|
|
|
// If this application is part of an operator, then we allow an implicit
|
|
// lvalue to be compatible with inout arguments. This is used by
|
|
// assignment operators.
|
|
TypeMatchKind ArgConv = TypeMatchKind::ArgumentTupleConversion;
|
|
if (isa<PrefixUnaryExpr>(anchor) || isa<PostfixUnaryExpr>(anchor) ||
|
|
isa<BinaryExpr>(anchor))
|
|
ArgConv = TypeMatchKind::OperatorArgumentTupleConversion;
|
|
|
|
// The argument type must be convertible to the input type.
|
|
if (matchTypes(func1->getInput(), func2->getInput(),
|
|
ArgConv, flags,
|
|
outerLocator.withPathElement(
|
|
ConstraintLocator::ApplyArgument))
|
|
== SolutionKind::Error)
|
|
return SolutionKind::Error;
|
|
|
|
// The result types are equivalent.
|
|
if (matchTypes(func1->getResult(), func2->getResult(),
|
|
TypeMatchKind::BindType,
|
|
flags,
|
|
locator.withPathElement(ConstraintLocator::FunctionResult))
|
|
== SolutionKind::Error)
|
|
return SolutionKind::Error;
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// For a metatype, perform a construction.
|
|
if (auto meta2 = dyn_cast<AnyMetatypeType>(desugar2)) {
|
|
auto instanceTy2 = meta2->getInstanceType();
|
|
|
|
// Bind the result type to the instance type.
|
|
if (matchTypes(func1->getResult(), instanceTy2,
|
|
TypeMatchKind::BindType,
|
|
flags,
|
|
locator.withPathElement(ConstraintLocator::ApplyFunction))
|
|
== SolutionKind::Error)
|
|
return SolutionKind::Error;
|
|
|
|
// Construct the instance from the input arguments.
|
|
addConstraint(ConstraintKind::Construction, func1->getInput(), instanceTy2,
|
|
getConstraintLocator(outerLocator));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
// If we're coming from an optional type, unwrap the optional and try again.
|
|
if (auto objectType2 = desugar2->getOptionalObjectType()) {
|
|
// Increase the score before we attempt a fix.
|
|
increaseScore(SK_Fix);
|
|
if (worseThanBestSolution())
|
|
return SolutionKind::Error;
|
|
|
|
Fixes.push_back({FixKind::ForceOptional,getConstraintLocator(locator)});
|
|
|
|
type2 = objectType2;
|
|
desugar2 = type2->getDesugaredType();
|
|
goto retry;
|
|
}
|
|
|
|
// If this is a '()' call, drop the call.
|
|
if (shouldAttemptFixes() &&
|
|
func1->getInput()->isEqual(TupleType::getEmpty(getASTContext()))) {
|
|
// Increase the score before we attempt a fix.
|
|
increaseScore(SK_Fix);
|
|
if (worseThanBestSolution())
|
|
return SolutionKind::Error;
|
|
|
|
// We don't bother with a 'None' case, because at this point we won't get
|
|
// a better diagnostic from that case.
|
|
Fixes.push_back({FixKind::RemoveNullaryCall,
|
|
getConstraintLocator(locator)});
|
|
|
|
return matchTypes(func1->getResult(), type2,
|
|
TypeMatchKind::BindType,
|
|
flags | TMF_ApplyingFix | TMF_GenerateConstraints,
|
|
locator.withPathElement(
|
|
ConstraintLocator::FunctionResult));
|
|
}
|
|
|
|
// If we are supposed to record failures, do so.
|
|
if (shouldRecordFailures()) {
|
|
recordFailure(getConstraintLocator(locator),
|
|
Failure::FunctionTypesMismatch,
|
|
type1, type2);
|
|
}
|
|
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
/// \brief Retrieve the type-matching kind corresponding to the given
|
|
/// constraint kind.
|
|
static TypeMatchKind getTypeMatchKind(ConstraintKind kind) {
|
|
switch (kind) {
|
|
case ConstraintKind::Bind: return TypeMatchKind::BindType;
|
|
case ConstraintKind::Equal: return TypeMatchKind::SameType;
|
|
case ConstraintKind::Subtype: return TypeMatchKind::Subtype;
|
|
case ConstraintKind::Conversion: return TypeMatchKind::Conversion;
|
|
case ConstraintKind::ArgumentTupleConversion:
|
|
return TypeMatchKind::ArgumentTupleConversion;
|
|
case ConstraintKind::OperatorArgumentTupleConversion:
|
|
return TypeMatchKind::OperatorArgumentTupleConversion;
|
|
case ConstraintKind::OperatorArgumentConversion:
|
|
return TypeMatchKind::OperatorArgumentConversion;
|
|
|
|
case ConstraintKind::ApplicableFunction:
|
|
llvm_unreachable("ApplicableFunction constraints don't involve "
|
|
"type matches");
|
|
|
|
case ConstraintKind::DynamicTypeOf:
|
|
llvm_unreachable("DynamicTypeOf constraints don't involve type matches");
|
|
|
|
case ConstraintKind::BindOverload:
|
|
llvm_unreachable("Overload binding constraints don't involve type matches");
|
|
|
|
case ConstraintKind::Construction:
|
|
llvm_unreachable("Construction constraints don't involve type matches");
|
|
|
|
case ConstraintKind::ConformsTo:
|
|
case ConstraintKind::SelfObjectOfProtocol:
|
|
llvm_unreachable("Conformance constraints don't involve type matches");
|
|
|
|
case ConstraintKind::CheckedCast:
|
|
llvm_unreachable("Checked cast constraints don't involve type matches");
|
|
|
|
case ConstraintKind::ValueMember:
|
|
case ConstraintKind::TypeMember:
|
|
llvm_unreachable("Member constraints don't involve type matches");
|
|
|
|
case ConstraintKind::Archetype:
|
|
case ConstraintKind::Class:
|
|
case ConstraintKind::BridgedToObjectiveC:
|
|
llvm_unreachable("Type properties don't involve type matches");
|
|
|
|
case ConstraintKind::Conjunction:
|
|
case ConstraintKind::Disjunction:
|
|
llvm_unreachable("Con/disjunction constraints don't involve type matches");
|
|
}
|
|
}
|
|
|
|
Type ConstraintSystem::getBaseTypeForArrayType(TypeBase *type) {
|
|
type = type->lookThroughAllAnyOptionalTypes().getPointer();
|
|
|
|
if (auto bound = type->getAs<BoundGenericStructType>()) {
|
|
if (bound->getDecl() == getASTContext().getArrayDecl()) {
|
|
return bound->getGenericArgs()[0];
|
|
}
|
|
}
|
|
|
|
type->dump();
|
|
llvm_unreachable("attempted to extract a base type from a non-array type");
|
|
}
|
|
|
|
/// Given that we have a conversion constraint between two types, and
|
|
/// that the given constraint-reduction rule applies between them at
|
|
/// the top level, apply it and generate any necessary recursive
|
|
/// constraints.
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyRestrictedConstraint(ConversionRestrictionKind restriction,
|
|
Type type1, Type type2,
|
|
TypeMatchKind matchKind,
|
|
unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
// Add to the score based on context.
|
|
auto addContextualScore = [&] {
|
|
// Okay, we need to perform one or more conversions. If this
|
|
// conversion will cause a function conversion, score it as worse.
|
|
// This induces conversions to occur within closures instead of
|
|
// outside of them wherever possible.
|
|
if (locator.isFunctionConversion()) {
|
|
increaseScore(SK_FunctionConversion);
|
|
}
|
|
};
|
|
|
|
// We'll apply user conversions for operator arguments at the application
|
|
// site.
|
|
if (matchKind == TypeMatchKind::OperatorArgumentConversion) {
|
|
flags |= TMF_ApplyingOperatorParameter;
|
|
}
|
|
|
|
unsigned subFlags = flags | TMF_GenerateConstraints;
|
|
switch (restriction) {
|
|
// for $< in { <, <c, <oc }:
|
|
// T_i $< U_i ===> (T_i...) $< (U_i...)
|
|
case ConversionRestrictionKind::TupleToTuple:
|
|
return matchTupleTypes(type1->castTo<TupleType>(),
|
|
type2->castTo<TupleType>(),
|
|
matchKind, subFlags, locator);
|
|
|
|
// T <c U ===> T <c (U)
|
|
case ConversionRestrictionKind::ScalarToTuple:
|
|
return matchScalarToTupleTypes(type1,
|
|
type2->castTo<TupleType>(),
|
|
matchKind, subFlags, locator);
|
|
|
|
// for $< in { <, <c, <oc }:
|
|
// T $< U ===> (T) $< U
|
|
case ConversionRestrictionKind::TupleToScalar:
|
|
return matchTupleToScalarTypes(type1->castTo<TupleType>(),
|
|
type2,
|
|
matchKind, subFlags, locator);
|
|
|
|
case ConversionRestrictionKind::DeepEquality:
|
|
return matchDeepEqualityTypes(type1, type2, locator);
|
|
|
|
case ConversionRestrictionKind::Superclass:
|
|
addContextualScore();
|
|
return matchSuperclassTypes(type1, type2, matchKind, subFlags, locator);
|
|
|
|
case ConversionRestrictionKind::LValueToRValue:
|
|
return matchTypes(type1->getRValueType(), type2,
|
|
matchKind, subFlags, locator);
|
|
|
|
// for $< in { <, <c, <oc }:
|
|
// T $< U, U : P_i ===> T $< protocol<P_i...>
|
|
case ConversionRestrictionKind::Existential:
|
|
addContextualScore();
|
|
return matchExistentialTypes(type1, type2,
|
|
matchKind, subFlags, locator);
|
|
|
|
// for $< in { <, <c, <oc }:
|
|
// T $< U ===> T $< U?
|
|
case ConversionRestrictionKind::ValueToOptional: {
|
|
addContextualScore();
|
|
assert(matchKind >= TypeMatchKind::Subtype);
|
|
auto generic2 = type2->castTo<BoundGenericType>();
|
|
assert(generic2->getDecl()->classifyAsOptionalType());
|
|
return matchTypes(type1, generic2->getGenericArgs()[0],
|
|
matchKind, (subFlags | TMF_UnwrappingOptional), locator);
|
|
}
|
|
|
|
// for $< in { <, <c, <oc }:
|
|
// T $< U ===> T? $< U?
|
|
// T $< U ===> T! $< U!
|
|
// T $< U ===> T! $< U?
|
|
// also:
|
|
// T <c U ===> T? <c U!
|
|
case ConversionRestrictionKind::OptionalToImplicitlyUnwrappedOptional:
|
|
case ConversionRestrictionKind::ImplicitlyUnwrappedOptionalToOptional:
|
|
case ConversionRestrictionKind::OptionalToOptional: {
|
|
addContextualScore();
|
|
assert(matchKind >= TypeMatchKind::Subtype);
|
|
auto generic1 = type1->castTo<BoundGenericType>();
|
|
auto generic2 = type2->castTo<BoundGenericType>();
|
|
assert(generic1->getDecl()->classifyAsOptionalType());
|
|
assert(generic2->getDecl()->classifyAsOptionalType());
|
|
return matchTypes(generic1->getGenericArgs()[0],
|
|
generic2->getGenericArgs()[0],
|
|
matchKind, subFlags, locator);
|
|
}
|
|
|
|
// T <c U ===> T! <c U
|
|
//
|
|
// We don't want to allow this after user-defined conversions:
|
|
// - it gets really complex for users to understand why there's
|
|
// a dereference in their code
|
|
// - it would allow nil to be coerceable to a non-optional type
|
|
// Fortunately, user-defined conversions only allow subtype
|
|
// conversions on their results.
|
|
case ConversionRestrictionKind::ForceUnchecked: {
|
|
addContextualScore();
|
|
assert(matchKind >= TypeMatchKind::Conversion);
|
|
auto boundGenericType1 = type1->castTo<BoundGenericType>();
|
|
assert(boundGenericType1->getDecl()->classifyAsOptionalType()
|
|
== OTK_ImplicitlyUnwrappedOptional);
|
|
assert(boundGenericType1->getGenericArgs().size() == 1);
|
|
Type valueType1 = boundGenericType1->getGenericArgs()[0];
|
|
increaseScore(SK_ForceUnchecked);
|
|
return matchTypes(valueType1, type2,
|
|
matchKind, subFlags, locator);
|
|
}
|
|
|
|
// T < U ===> Array<T> <c Array<U>
|
|
case ConversionRestrictionKind::ArrayUpcast: {
|
|
auto t1 = type1->getDesugaredType();
|
|
auto t2 = type2->getDesugaredType();
|
|
|
|
Type baseType1 = this->getBaseTypeForArrayType(t1);
|
|
Type baseType2 = this->getBaseTypeForArrayType(t2);
|
|
|
|
// Look through type variables in the first base type; we need to know it's
|
|
// structure before we can decide whether this can be an array upcast.
|
|
TypeVariableType *baseTypeVar1;
|
|
baseType1 = getFixedTypeRecursive(baseType1, baseTypeVar1, false, false);
|
|
if (baseTypeVar1) {
|
|
if (flags & TMF_GenerateConstraints) {
|
|
addConstraint(
|
|
Constraint::createRestricted(*this, getConstraintKind(matchKind),
|
|
restriction, type1, type2,
|
|
getConstraintLocator(locator)));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
return SolutionKind::Unsolved;
|
|
}
|
|
|
|
// The first base type must be a class or ObjC class existential.
|
|
if (!baseType1->getClassOrBoundGenericClass() &&
|
|
!baseType1->isObjCExistentialType()) {
|
|
// FIXME: Record failure.
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
increaseScore(SK_ArrayUpcastConversion);
|
|
addContextualScore();
|
|
assert(matchKind >= TypeMatchKind::Conversion);
|
|
|
|
return matchTypes(baseType1,
|
|
baseType2,
|
|
TypeMatchKind::Subtype,
|
|
subFlags,
|
|
locator);
|
|
}
|
|
|
|
// T < U ===> Array<T> is bridged to Array<U>
|
|
case ConversionRestrictionKind::ArrayBridged: {
|
|
auto t1 = type1->getDesugaredType();
|
|
auto t2 = type2->getDesugaredType();
|
|
|
|
Type baseType1 = this->getBaseTypeForArrayType(t1);
|
|
Type baseType2 = this->getBaseTypeForArrayType(t2);
|
|
|
|
// Look through type variables in the first base type; we need to know it's
|
|
// structure before we can decide whether this can be an array bridge.
|
|
TypeVariableType *baseTypeVar1;
|
|
baseType1 = getFixedTypeRecursive(baseType1, baseTypeVar1, false, false);
|
|
if (baseTypeVar1) {
|
|
if (flags & TMF_GenerateConstraints) {
|
|
addConstraint(
|
|
Constraint::createRestricted(*this, getConstraintKind(matchKind),
|
|
restriction, type1, type2,
|
|
getConstraintLocator(locator)));
|
|
return SolutionKind::Solved;
|
|
}
|
|
|
|
return SolutionKind::Unsolved;
|
|
}
|
|
|
|
increaseScore(SK_ArrayBridgedConversion);
|
|
addContextualScore();
|
|
assert(matchKind >= TypeMatchKind::Conversion);
|
|
|
|
// Allow assignments from Array<T> to Array<U> if T is bridged
|
|
// non-verbatim to type U.
|
|
Type bridgedType1;
|
|
bool bridgedVerbatim;
|
|
std::tie(bridgedType1, bridgedVerbatim) = TC.getBridgedToObjC(DC,baseType1);
|
|
if (bridgedType1 && !bridgedVerbatim) {
|
|
// Check if T's bridged type is a subtype of U.
|
|
return matchTypes(bridgedType1,
|
|
baseType2,
|
|
TypeMatchKind::Subtype,
|
|
subFlags,
|
|
locator);
|
|
}
|
|
|
|
// FIXME: Record failure.
|
|
return ConstraintSystem::SolutionKind::Error;
|
|
}
|
|
|
|
// T' < U, hasMember(T, conversion, T -> T') ===> T <c U
|
|
case ConversionRestrictionKind::User:
|
|
addContextualScore();
|
|
assert(matchKind >= TypeMatchKind::Conversion);
|
|
return tryUserConversion(*this, type1,
|
|
ConstraintKind::Subtype,
|
|
type2,
|
|
locator);
|
|
|
|
}
|
|
|
|
llvm_unreachable("bad conversion restriction");
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyFixConstraint(Fix fix,
|
|
Type type1, Type type2,
|
|
TypeMatchKind matchKind,
|
|
unsigned flags,
|
|
ConstraintLocatorBuilder locator) {
|
|
auto &ctx = getASTContext();
|
|
if (ctx.LangOpts.DebugConstraintSolver) {
|
|
auto &log = ctx.TypeCheckerDebug->getStream();
|
|
log.indent(solverState? solverState->depth * 2 + 2 : 0)
|
|
<< "(attempting fix ";
|
|
fix.print(log, this);
|
|
log << " @";
|
|
getConstraintLocator(locator)->dump(&ctx.SourceMgr, log);
|
|
log << ")\n";
|
|
}
|
|
|
|
// Record the fix.
|
|
if (fix.getKind() != FixKind::None) {
|
|
Fixes.push_back({fix, getConstraintLocator(locator)});
|
|
|
|
// Increase the score. If this would make the current solution worse than
|
|
// the best solution we've seen already, stop now.
|
|
increaseScore(SK_Fix);
|
|
if (worseThanBestSolution())
|
|
return SolutionKind::Error;
|
|
}
|
|
|
|
// Try with the fix.
|
|
unsigned subFlags = flags | TMF_ApplyingFix | TMF_GenerateConstraints;
|
|
switch (fix.getKind()) {
|
|
case FixKind::None:
|
|
return matchTypes(type1, type2, matchKind, subFlags, locator);
|
|
|
|
case FixKind::NullaryCall: {
|
|
// Assume that '()' was applied to the first type.
|
|
|
|
// If the function was actually in a tuple, tuple'ify the
|
|
// FIXME: This is yet another awful hack due to one-element tuples.
|
|
auto funcTy = type1->getRValueType();
|
|
Identifier nameToAdd;
|
|
if (auto tupleTy = funcTy->getAs<TupleType>()) {
|
|
int scalarIdx = tupleTy->getFieldForScalarInit();
|
|
nameToAdd = tupleTy->getFields()[scalarIdx].getName();
|
|
funcTy = tupleTy->getElementType(scalarIdx);
|
|
}
|
|
|
|
if (auto optObjectTy = funcTy->getAnyOptionalObjectType())
|
|
funcTy = optObjectTy;
|
|
|
|
auto resultTy = funcTy->castTo<AnyFunctionType>()->getResult();
|
|
if (!nameToAdd.empty())
|
|
resultTy = TupleType::get(TupleTypeElt(resultTy, nameToAdd),
|
|
getASTContext());
|
|
return matchTypes(resultTy, type2, matchKind, subFlags, locator);
|
|
}
|
|
|
|
case FixKind::RemoveNullaryCall:
|
|
llvm_unreachable("Always applied directly");
|
|
|
|
case FixKind::ForceOptional:
|
|
// Assume that '!' was applied to the first type.
|
|
return matchTypes(type1->getRValueType()->getOptionalObjectType(), type2,
|
|
matchKind, subFlags, locator);
|
|
|
|
case FixKind::ForceDowncast:
|
|
// These work whenever they are suggested.
|
|
return SolutionKind::Solved;
|
|
|
|
case FixKind::AddressOf:
|
|
// Assume that '&' was applied to the first type, turning an lvalue into
|
|
// an inout.
|
|
return matchTypes(InOutType::get(type1->getRValueType()), type2,
|
|
matchKind, subFlags, locator);
|
|
|
|
case FixKind::TupleToScalar:
|
|
return matchTypes(type1->castTo<TupleType>()->getElementType(0),
|
|
type2, matchKind, subFlags,
|
|
locator.withPathElement(
|
|
LocatorPathElt::getTupleElement(0)));
|
|
|
|
case FixKind::ScalarToTuple: {
|
|
auto tuple2 = type2->castTo<TupleType>();
|
|
int scalarFieldIdx = tuple2->getFieldForScalarInit();
|
|
assert(scalarFieldIdx >= 0 && "Invalid tuple for scalar-to-tuple");
|
|
const auto &elt = tuple2->getFields()[scalarFieldIdx];
|
|
auto scalarFieldTy = elt.isVararg()? elt.getVarargBaseTy() : elt.getType();
|
|
return matchTypes(type1, scalarFieldTy, matchKind, subFlags,
|
|
locator.withPathElement(
|
|
ConstraintLocator::ScalarToTuple));
|
|
}
|
|
|
|
case FixKind::RelabelCallTuple:
|
|
// The actual semantics are handled elsewhere.
|
|
return SolutionKind::Solved;
|
|
}
|
|
}
|
|
|
|
ConstraintSystem::SolutionKind
|
|
ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
|
|
switch (constraint.getKind()) {
|
|
case ConstraintKind::Bind:
|
|
case ConstraintKind::Equal:
|
|
case ConstraintKind::Subtype:
|
|
case ConstraintKind::Conversion:
|
|
case ConstraintKind::ArgumentTupleConversion:
|
|
case ConstraintKind::OperatorArgumentTupleConversion:
|
|
case ConstraintKind::OperatorArgumentConversion: {
|
|
// For relational constraints, match up the types.
|
|
auto matchKind = getTypeMatchKind(constraint.getKind());
|
|
|
|
// If there is a fix associated with this constraint, apply it before
|
|
// we continue.
|
|
if (auto fix = constraint.getFix()) {
|
|
return simplifyFixConstraint(*fix, constraint.getFirstType(),
|
|
constraint.getSecondType(), matchKind,
|
|
TMF_GenerateConstraints,
|
|
constraint.getLocator());
|
|
}
|
|
|
|
|
|
// If there is a restriction on this constraint, apply it directly rather
|
|
// than going through the general \c matchTypes() machinery.
|
|
if (auto restriction = constraint.getRestriction()) {
|
|
SolutionKind result = simplifyRestrictedConstraint(*restriction,
|
|
constraint.getFirstType(),
|
|
constraint.getSecondType(),
|
|
matchKind,
|
|
0,
|
|
constraint.getLocator());
|
|
|
|
// If we actually solved something, record what we did.
|
|
switch(result) {
|
|
case SolutionKind::Error:
|
|
case SolutionKind::Unsolved:
|
|
break;
|
|
|
|
case SolutionKind::Solved:
|
|
ConstraintRestrictions.push_back(
|
|
std::make_tuple(constraint.getFirstType(),
|
|
constraint.getSecondType(), *restriction));
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return matchTypes(constraint.getFirstType(), constraint.getSecondType(),
|
|
matchKind,
|
|
TMF_None, constraint.getLocator());
|
|
}
|
|
|
|
case ConstraintKind::ApplicableFunction:
|
|
return simplifyApplicableFnConstraint(constraint);
|
|
|
|
case ConstraintKind::DynamicTypeOf:
|
|
return simplifyDynamicTypeOfConstraint(constraint);
|
|
|
|
case ConstraintKind::BindOverload:
|
|
resolveOverload(constraint.getLocator(), constraint.getFirstType(),
|
|
constraint.getOverloadChoice());
|
|
return SolutionKind::Solved;
|
|
|
|
case ConstraintKind::Construction:
|
|
return simplifyConstructionConstraint(constraint.getSecondType(),
|
|
constraint.getFirstType(),
|
|
TMF_None,
|
|
constraint.getLocator());
|
|
|
|
case ConstraintKind::ConformsTo:
|
|
case ConstraintKind::SelfObjectOfProtocol:
|
|
return simplifyConformsToConstraint(
|
|
constraint.getFirstType(),
|
|
constraint.getProtocol(),
|
|
constraint.getLocator(),
|
|
TMF_None,
|
|
constraint.getKind() == ConstraintKind::SelfObjectOfProtocol);
|
|
|
|
case ConstraintKind::CheckedCast:
|
|
return simplifyCheckedCastConstraint(constraint.getFirstType(),
|
|
constraint.getSecondType(),
|
|
constraint.getLocator());
|
|
|
|
case ConstraintKind::ValueMember:
|
|
case ConstraintKind::TypeMember:
|
|
return simplifyMemberConstraint(constraint);
|
|
|
|
case ConstraintKind::Archetype:
|
|
return simplifyArchetypeConstraint(constraint);
|
|
|
|
case ConstraintKind::BridgedToObjectiveC:
|
|
return simplifyBridgedToObjectiveCConstraint(constraint);
|
|
|
|
case ConstraintKind::Class:
|
|
return simplifyClassConstraint(constraint);
|
|
|
|
case ConstraintKind::Conjunction:
|
|
// Process all of the constraints in the conjunction.
|
|
for (auto con : constraint.getNestedConstraints()) {
|
|
addConstraint(con);
|
|
if (failedConstraint)
|
|
return SolutionKind::Error;
|
|
}
|
|
return SolutionKind::Solved;
|
|
|
|
case ConstraintKind::Disjunction:
|
|
// Disjunction constraints are never solved here.
|
|
return SolutionKind::Unsolved;
|
|
}
|
|
}
|