Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
5946 lines (5097 sloc) 223 KB
//===--- CSSimplify.cpp - Constraint Simplification -----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements simplifications of constraints within the constraint
// system.
//
//===----------------------------------------------------------------------===//
#include "CSFix.h"
#include "ConstraintSystem.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/Basic/StringExtras.h"
#include "swift/ClangImporter/ClangModule.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/Compiler.h"
using namespace swift;
using namespace constraints;
MatchCallArgumentListener::~MatchCallArgumentListener() { }
void MatchCallArgumentListener::extraArgument(unsigned argIdx) { }
void MatchCallArgumentListener::missingArgument(unsigned paramIdx) { }
bool MatchCallArgumentListener::missingLabel(unsigned paramIdx) { return true; }
bool MatchCallArgumentListener::extraneousLabel(unsigned paramIdx) {
return true;
}
bool MatchCallArgumentListener::incorrectLabel(unsigned paramIdx) {
return true;
}
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-typo'd 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;
// 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 None;
// The distance can be zero due to the "with" transformation above.
if (dist == 0)
return 1;
// If this is just a single character label on both sides,
// simply return distance.
if (paramName.size() == 1 && argName.size() == 1)
return dist;
// 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 None;
return dist;
}
bool constraints::
areConservativelyCompatibleArgumentLabels(ValueDecl *decl,
bool hasCurriedSelf,
ArrayRef<Identifier> labels,
bool hasTrailingClosure) {
// Bail out conservatively if this isn't a function declaration.
auto fn = dyn_cast<AbstractFunctionDecl>(decl);
if (!fn) return true;
auto *fTy = fn->getInterfaceType()->castTo<AnyFunctionType>();
SmallVector<AnyFunctionType::Param, 8> argInfos;
for (auto argLabel : labels) {
argInfos.push_back(AnyFunctionType::Param(Type(), argLabel, {}));
}
const AnyFunctionType *levelTy = fTy;
if (hasCurriedSelf) {
levelTy = levelTy->getResult()->getAs<AnyFunctionType>();
assert(levelTy && "Parameter list curry level does not match type");
}
auto params = levelTy->getParams();
SmallBitVector defaultMap =
computeDefaultMap(params, decl, hasCurriedSelf);
MatchCallArgumentListener listener;
SmallVector<ParamBinding, 8> unusedParamBindings;
return !matchCallArguments(argInfos, params, defaultMap,
hasTrailingClosure,
/*allow fixes*/ false,
listener, unusedParamBindings);
}
/// Determine the default type-matching options to use when decomposing a
/// constraint into smaller constraints.
static ConstraintSystem::TypeMatchOptions getDefaultDecompositionOptions(
ConstraintSystem::TypeMatchOptions flags) {
return flags | ConstraintSystem::TMF_GenerateConstraints;
}
// FIXME: This should return ConstraintSystem::TypeMatchResult instead
// to give more information to the solver about the failure.
bool constraints::
matchCallArguments(ArrayRef<AnyFunctionType::Param> args,
ArrayRef<AnyFunctionType::Param> params,
const SmallBitVector &defaultMap,
bool hasTrailingClosure,
bool allowFixes,
MatchCallArgumentListener &listener,
SmallVectorImpl<ParamBinding> &parameterBindings) {
assert(params.size() == defaultMap.size() && "Default map does not match");
// Keep track of the parameter we're matching and what argument indices
// got bound to each parameter.
unsigned paramIdx, numParams = params.size();
parameterBindings.clear();
parameterBindings.resize(numParams);
// Keep track of which arguments we have claimed from the argument tuple.
unsigned nextArgIdx = 0, numArgs = args.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;
auto hasDefault = [&defaultMap, &numParams](unsigned idx) -> bool {
return idx < numParams ? defaultMap.test(idx) : false;
};
// Local function that claims the argument at \c argNumber, returning the
// index of the claimed argument. This is primarily a helper for
// \c claimNextNamed.
auto claim = [&](Identifier expectedName, unsigned argNumber,
bool ignoreNameClash = false) -> unsigned {
// Make sure we can claim this argument.
assert(argNumber != numArgs && "Must have a valid index to claim");
assert(!claimedArgs[argNumber] && "Argument already claimed");
if (!actualArgNames.empty()) {
// We're recording argument names; record this one.
actualArgNames[argNumber] = expectedName;
} else if (args[argNumber].getLabel() != 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 &param = params[i];
bool firstArg = true;
for (auto argIdx : parameterBindings[i]) {
actualArgNames[argIdx] = firstArg ? param.getLabel() : Identifier();
firstArg = false;
}
}
// Record this argument name.
actualArgNames[argNumber] = expectedName;
}
claimedArgs[argNumber] = true;
++numClaimedArgs;
return argNumber;
};
// 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 paramLabel, bool ignoreNameMismatch,
bool forVariadic = false) -> Optional<unsigned> {
// Skip over any claimed arguments.
skipClaimedArgs();
// If we've claimed all of the arguments, there's nothing more to do.
if (numClaimedArgs == numArgs)
return None;
// Go hunting for an unclaimed argument whose name does match.
Optional<unsigned> claimedWithSameName;
for (unsigned i = nextArgIdx; i != numArgs; ++i) {
auto argLabel = args[i].getLabel();
if (argLabel != paramLabel) {
// If this is an attempt to claim additional unlabeled arguments
// for variadic parameter, we have to stop at first labeled argument.
if (forVariadic)
return None;
// Otherwise we can continue trying to find argument which
// matches parameter with or without label.
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. If the match wasn't the next one, we have
// potentially out of order arguments.
if (i != nextArgIdx) {
// Avoid claiming un-labeled defaulted parameters
// by out-of-order un-labeled arguments or parts
// of variadic argument sequence, because that might
// be incorrect:
// ```swift
// func foo(_ a: Int, _ b: Int = 0, c: Int = 0, _ d: Int) {}
// foo(1, c: 2, 3) // -> `3` will be claimed as '_ b:'.
// ```
if (argLabel.empty() && (hasDefault(i) || !forVariadic))
continue;
potentiallyOutOfOrder = true;
}
// Claim it.
return claim(paramLabel, i);
}
// If we're not supposed to attempt any fixes, we're done.
if (!allowFixes)
return None;
// 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 argument might have extraneous label, in which case we try to
// fix the problem by removing such label.
// - The keyword argument might be a typo for an actual argument name, in
// which case we should find the closest match to correct to.
// Missing or extraneous label.
if (nextArgIdx != numArgs && ignoreNameMismatch) {
auto argLabel = args[nextArgIdx].getLabel();
// Claim this argument if we are asked to ignore labeling failure,
// only if argument doesn't have a label when parameter expected
// it to, or vice versa.
if (paramLabel.empty() || argLabel.empty())
return claim(paramLabel, nextArgIdx);
}
// Redundant keyword arguments.
if (claimedWithSameName) {
// FIXME: We can provide better diagnostics here.
return None;
}
// Typo correction is handled in a later pass.
return None;
};
// Local function that attempts to bind the given parameter to arguments in
// the list.
bool haveUnfulfilledParams = false;
auto bindNextParameter = [&](bool ignoreNameMismatch) {
const auto &param = params[paramIdx];
// Handle variadic parameters.
if (param.isVariadic()) {
// Claim the next argument with the name of this parameter.
auto claimed = claimNextNamed(param.getLabel(), ignoreNameMismatch);
// If there was no such argument, leave the parameter unfulfilled.
if (!claimed) {
haveUnfulfilledParams = true;
return;
}
// Record the first argument for the variadic.
parameterBindings[paramIdx].push_back(*claimed);
auto currentNextArgIdx = nextArgIdx;
{
nextArgIdx = *claimed;
// Claim any additional unnamed arguments.
while ((claimed = claimNextNamed(Identifier(), false, true))) {
parameterBindings[paramIdx].push_back(*claimed);
}
}
nextArgIdx = currentNextArgIdx;
skipClaimedArgs();
return;
}
// Try to claim an argument for this parameter.
if (auto claimed = claimNextNamed(param.getLabel(), ignoreNameMismatch)) {
parameterBindings[paramIdx].push_back(*claimed);
skipClaimedArgs();
return;
}
// There was no argument to claim. Leave the argument unfulfilled.
haveUnfulfilledParams = true;
};
// If we have a trailing closure, it maps to the last parameter.
if (hasTrailingClosure && numParams > 0) {
claimedArgs[numArgs-1] = true;
++numClaimedArgs;
parameterBindings[numParams-1].push_back(numArgs-1);
}
// Mark through the parameters, binding them to their arguments.
for (paramIdx = 0; paramIdx != numParams; ++paramIdx) {
if (parameterBindings[paramIdx].empty())
bindNextParameter(false);
}
// If we have any unclaimed arguments, complain about those.
if (numClaimedArgs != numArgs) {
// Find all of the named, unclaimed arguments.
llvm::SmallVector<unsigned, 4> unclaimedNamedArgs;
for (nextArgIdx = 0; skipClaimedArgs(), nextArgIdx != numArgs;
++nextArgIdx) {
if (!args[nextArgIdx].getLabel().empty())
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 (params[paramIdx].getLabel().empty())
hasUnfulfilledUnnamedParams = true;
else
unfulfilledNamedParams.push_back(paramIdx);
}
}
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 = args[argIdx].getLabel();
// 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 = params[param].getLabel();
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];
auto paramLabel = params[paramIdx].getLabel();
parameterBindings[paramIdx].push_back(claim(paramLabel, argIdx));
skipClaimedArgs();
// 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 there are as many arguments as parameters but we still
// haven't claimed all of the arguments, it could mean that
// labels don't line up, if so let's try to claim arguments
// with incorrect labels, and let OoO/re-labeling logic diagnose that.
if (numArgs == numParams && numClaimedArgs != numArgs) {
for (unsigned i = 0; i < numArgs; ++i) {
if (claimedArgs[i] || !parameterBindings[i].empty())
continue;
// If parameter has a default value, we don't really
// now if label doesn't match because it's incorrect
// or argument belongs to some other parameter, so
// we just leave this parameter unfulfilled.
if (defaultMap.test(i))
continue;
// Looks like there was no parameter claimed at the same
// position, it could only mean that label is completely
// different, because typo correction has been attempted already.
parameterBindings[i].push_back(claim(params[i].getLabel(), i));
}
}
// 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 &param = params[paramIdx];
// Variadic parameters can be unfulfilled.
if (param.isVariadic())
continue;
// Parameters with defaults can be unfulfilled.
if (hasDefault(paramIdx))
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) {
unsigned argIdx = 0;
// Enumerate the parameters and their bindings to see if any arguments are
// our of order
for (auto binding : parameterBindings) {
for (auto boundArgIdx : binding) {
// We've found the parameter that has an out of order
// argument, and know the indices of the argument that
// needs to move (fromArgIdx) and the argument location
// it should move to (toArgIdx).
auto fromArgIdx = boundArgIdx;
auto toArgIdx = argIdx;
// If there is no re-ordering going on, and index is past
// the number of parameters, it could only mean that this
// is variadic parameter, so let's just move on.
if (fromArgIdx == toArgIdx && toArgIdx >= params.size()) {
assert(args[fromArgIdx].getLabel().empty());
argIdx++;
continue;
}
// First let's double check if out-of-order argument is nothing
// more than a simple label mismatch, because in situation where
// one argument requires label and another one doesn't, but caller
// doesn't provide either, problem is going to be identified as
// out-of-order argument instead of label mismatch.
auto expectedLabel = params[toArgIdx].getLabel();
auto argumentLabel = args[fromArgIdx].getLabel();
if (argumentLabel != expectedLabel) {
// - The parameter is unnamed, in which case we try to fix the
// problem by removing the name.
if (expectedLabel.empty()) {
if (listener.extraneousLabel(toArgIdx))
return true;
// - The argument is unnamed, in which case we try to fix the
// problem by adding the name.
} else if (argumentLabel.empty()) {
if (listener.missingLabel(toArgIdx))
return true;
// - The argument label has a typo at the same position.
} else if (fromArgIdx == toArgIdx &&
listener.incorrectLabel(toArgIdx)) {
return true;
}
}
if (boundArgIdx == argIdx) {
// If the argument is in the right location, just continue
argIdx++;
continue;
}
listener.outOfOrderArgument(fromArgIdx, toArgIdx);
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);
}
/// Find the callee declaration and uncurry level for a given call
/// locator.
static std::tuple<ValueDecl *, bool, ArrayRef<Identifier>, bool>
getCalleeDeclAndArgs(ConstraintSystem &cs,
ConstraintLocatorBuilder callLocator,
SmallVectorImpl<Identifier> &argLabelsScratch) {
ArrayRef<Identifier> argLabels;
bool hasTrailingClosure = false;
// Break down the call.
SmallVector<LocatorPathElt, 2> path;
auto callExpr = callLocator.getLocatorParts(path);
if (!callExpr)
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
// Our remaining path can only be 'ApplyArgument'.
if (!path.empty() &&
!(path.size() <= 2 &&
path.back().getKind() == ConstraintLocator::ApplyArgument))
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
// Dig out the callee.
ConstraintLocator *targetLocator;
if (auto call = dyn_cast<CallExpr>(callExpr)) {
targetLocator = cs.getConstraintLocator(call->getDirectCallee());
argLabels = call->getArgumentLabels();
hasTrailingClosure = call->hasTrailingClosure();
} else if (auto unresolved = dyn_cast<UnresolvedMemberExpr>(callExpr)) {
targetLocator = cs.getConstraintLocator(callExpr);
argLabels = unresolved->getArgumentLabels();
hasTrailingClosure = unresolved->hasTrailingClosure();
} else if (auto subscript = dyn_cast<SubscriptExpr>(callExpr)) {
targetLocator = cs.getConstraintLocator(callExpr);
argLabels = subscript->getArgumentLabels();
hasTrailingClosure = subscript->hasTrailingClosure();
} else if (auto dynSubscript = dyn_cast<DynamicSubscriptExpr>(callExpr)) {
targetLocator = cs.getConstraintLocator(callExpr);
argLabels = dynSubscript->getArgumentLabels();
hasTrailingClosure = dynSubscript->hasTrailingClosure();
} else if (auto keyPath = dyn_cast<KeyPathExpr>(callExpr)) {
if (path.size() != 2 ||
path[0].getKind() != ConstraintLocator::KeyPathComponent ||
path[1].getKind() != ConstraintLocator::ApplyArgument)
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
auto componentIndex = path[0].getValue();
if (componentIndex >= keyPath->getComponents().size())
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
auto &component = keyPath->getComponents()[componentIndex];
switch (component.getKind()) {
case KeyPathExpr::Component::Kind::Subscript:
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
targetLocator = cs.getConstraintLocator(callExpr, path[0]);
argLabels = component.getSubscriptLabels();
hasTrailingClosure = false; // key paths don't support trailing closures
break;
case KeyPathExpr::Component::Kind::Invalid:
case KeyPathExpr::Component::Kind::UnresolvedProperty:
case KeyPathExpr::Component::Kind::Property:
case KeyPathExpr::Component::Kind::OptionalForce:
case KeyPathExpr::Component::Kind::OptionalChain:
case KeyPathExpr::Component::Kind::OptionalWrap:
case KeyPathExpr::Component::Kind::Identity:
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
}
} else {
if (auto apply = dyn_cast<ApplyExpr>(callExpr)) {
argLabels = apply->getArgumentLabels(argLabelsScratch);
assert(!apply->hasTrailingClosure());
} else if (auto objectLiteral = dyn_cast<ObjectLiteralExpr>(callExpr)) {
argLabels = objectLiteral->getArgumentLabels();
hasTrailingClosure = objectLiteral->hasTrailingClosure();
}
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
}
// Find the overload choice corresponding to the callee locator.
// FIXME: This linearly walks the list of resolved overloads, which is
// potentially very expensive.
Optional<OverloadChoice> choice;
for (auto resolved = cs.getResolvedOverloadSets(); resolved;
resolved = resolved->Previous) {
// FIXME: Workaround null locators.
if (!resolved->Locator) continue;
auto resolvedLocator = resolved->Locator;
SmallVector<LocatorPathElt, 4> resolvedPath(
resolvedLocator->getPath().begin(),
resolvedLocator->getPath().end());
if (!resolvedPath.empty() &&
(resolvedPath.back().getKind() == ConstraintLocator::SubscriptMember ||
resolvedPath.back().getKind() == ConstraintLocator::Member ||
resolvedPath.back().getKind() == ConstraintLocator::UnresolvedMember ||
resolvedPath.back().getKind() ==
ConstraintLocator::ConstructorMember)) {
resolvedPath.pop_back();
resolvedLocator = cs.getConstraintLocator(
resolvedLocator->getAnchor(),
resolvedPath,
resolvedLocator->getSummaryFlags());
}
SourceRange range;
resolvedLocator = simplifyLocator(cs, resolvedLocator, range);
if (resolvedLocator == targetLocator) {
choice = resolved->Choice;
break;
}
}
// If we didn't find any matching overloads, we're done.
if (!choice)
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
// If there's a declaration, return it.
if (choice->isDecl()) {
auto decl = choice->getDecl();
bool hasCurriedSelf = false;
if (decl->getDeclContext()->isTypeContext()) {
if (auto function = dyn_cast<AbstractFunctionDecl>(decl)) {
// References to instance members on a metatype stay at level 0.
// Everything else is level 1.
if (!(function->isInstanceMember() &&
cs.getFixedTypeRecursive(choice->getBaseType(),
/*wantRValue=*/true)
->is<AnyMetatypeType>()))
hasCurriedSelf = true;
} else if (isa<SubscriptDecl>(decl)) {
// Subscript level 1 == the indices.
hasCurriedSelf = true;
} else if (isa<EnumElementDecl>(decl)) {
// Enum element level 1 == the payload.
hasCurriedSelf = true;
}
}
return std::make_tuple(decl, hasCurriedSelf, argLabels, hasTrailingClosure);
}
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
hasTrailingClosure);
}
class ArgumentFailureTracker : public MatchCallArgumentListener {
ConstraintSystem &CS;
ConstraintLocatorBuilder Locator;
public:
ArgumentFailureTracker(ConstraintSystem &cs, ConstraintLocatorBuilder locator)
: CS(cs), Locator(locator) {}
bool missingLabel(unsigned paramIndex) override {
return !CS.shouldAttemptFixes();
}
bool extraneousLabel(unsigned paramIndex) override {
return !CS.shouldAttemptFixes();
}
bool incorrectLabel(unsigned paramIndex) override {
return !CS.shouldAttemptFixes();
}
bool relabelArguments(ArrayRef<Identifier> newLabels) override {
if (!CS.shouldAttemptFixes())
return true;
auto *anchor = Locator.getBaseLocator()->getAnchor();
if (!anchor || !isa<CallExpr>(anchor))
return true;
auto *locator = CS.getConstraintLocator(anchor);
auto *fix = RelabelArguments::create(CS, newLabels, locator);
CS.recordFix(fix);
return false;
}
};
// Match the argument of a call to the parameter.
ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
ConstraintSystem &cs, ArrayRef<AnyFunctionType::Param> args,
ArrayRef<AnyFunctionType::Param> params, ConstraintLocatorBuilder locator) {
// Extract the parameters.
ValueDecl *callee;
bool hasCurriedSelf;
ArrayRef<Identifier> argLabels;
SmallVector<Identifier, 2> argLabelsScratch;
bool hasTrailingClosure = false;
std::tie(callee, hasCurriedSelf, argLabels, hasTrailingClosure) =
getCalleeDeclAndArgs(cs, locator, argLabelsScratch);
SmallBitVector defaultMap =
computeDefaultMap(params, callee, hasCurriedSelf);
// Apply labels to arguments.
SmallVector<AnyFunctionType::Param, 8> argsWithLabels;
argsWithLabels.append(args.begin(), args.end());
AnyFunctionType::relabelParams(argsWithLabels, argLabels);
// FIXME: Remove this. It's functionally identical to the real code
// path below, except for some behavioral differences in solution ranking
// that I don't understand.
if (params.size() == 1 &&
args.size() == 1 &&
params[0].getLabel().empty() &&
args[0].getLabel().empty() &&
!params[0].getParameterFlags().isInOut() &&
!args[0].getParameterFlags().isInOut() &&
params[0].getPlainType()->isAny()) {
auto argType = args[0].getPlainType();
// Disallow assignment of noescape function to parameter of type
// Any. Allowing this would allow these functions to escape.
if (auto *fnTy = argType->getAs<AnyFunctionType>()) {
if (fnTy->isNoEscape()) {
auto *loc = cs.getConstraintLocator(locator);
// Allow assigned of 'no-escape' function with recorded fix.
if (cs.shouldAttemptFixes()) {
if (!cs.recordFix(MarkExplicitlyEscaping::create(cs, loc)))
return cs.getTypeMatchSuccess();
}
return cs.getTypeMatchFailure(locator);
}
}
return cs.getTypeMatchSuccess();
}
// Match up the call arguments to the parameters.
ArgumentFailureTracker listener(cs, locator);
SmallVector<ParamBinding, 4> parameterBindings;
if (constraints::matchCallArguments(argsWithLabels, params,
defaultMap,
hasTrailingClosure,
cs.shouldAttemptFixes(), listener,
parameterBindings))
return cs.getTypeMatchFailure(locator);
// 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.
auto *anchor = locator.getAnchor();
assert(anchor && "locator without anchor expression?");
bool isOperator = (isa<PrefixUnaryExpr>(anchor) ||
isa<PostfixUnaryExpr>(anchor) || isa<BinaryExpr>(anchor));
ConstraintKind subKind = (isOperator
? ConstraintKind::OperatorArgumentConversion
: ConstraintKind::ArgumentConversion);
// Check whether argument of the call at given position refers to
// parameter marked as `@autoclosure`. This function is used to
// maintain source compatibility with Swift versions < 5,
// previously examples like following used to type-check:
//
// func foo(_ x: @autoclosure () -> Int) {}
// func bar(_ y: @autoclosure () -> Int) {
// foo(y)
// }
auto isAutoClosureArg = [&](Expr *anchor, unsigned argIdx) -> bool {
assert(anchor);
auto *argExpr = getArgumentExpr(anchor, argIdx);
if (!argExpr)
return false;
if (auto *DRE = dyn_cast<DeclRefExpr>(argExpr)) {
if (auto *param = dyn_cast<ParamDecl>(DRE->getDecl()))
return param->isAutoClosure();
}
return false;
};
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 &param = params[paramIdx];
auto paramTy = param.getOldType();
if (param.isAutoClosure())
paramTy = paramTy->castTo<FunctionType>()->getResult();
// Compare each of the bound arguments for this parameter.
for (auto argIdx : parameterBindings[paramIdx]) {
auto loc = locator.withPathElement(LocatorPathElt::
getApplyArgToParam(argIdx,
paramIdx));
auto argTy = argsWithLabels[argIdx].getOldType();
// If parameter was marked as `@autoclosure` and argument
// is itself `@autoclosure` function type in Swift < 5,
// let's fix that up by making it look like argument is
// called implicitly.
if (param.isAutoClosure() &&
isAutoClosureArg(locator.getAnchor(), argIdx)) {
argTy = argTy->castTo<FunctionType>()->getResult();
cs.increaseScore(SK_FunctionConversion);
if (cs.getASTContext().isSwiftVersionAtLeast(5)) {
auto *fixLoc = cs.getConstraintLocator(loc);
if (cs.recordFix(AutoClosureForwarding::create(cs, fixLoc)))
return cs.getTypeMatchFailure(loc);
}
}
// If argument comes for declaration it should loose
// `@autoclosure` flag, because in context it's used
// as a function type represented by autoclosure.
assert(!argsWithLabels[argIdx].isAutoClosure());
cs.addConstraint(
subKind, argTy, paramTy,
param.isAutoClosure()
? loc.withPathElement(ConstraintLocator::AutoclosureResult)
: loc,
/*isFavored=*/false);
}
}
return cs.getTypeMatchSuccess();
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
ConstraintKind kind, TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
// FIXME: Remove varargs logic below once we're no longer comparing
// argument lists in CSRanking.
// Equality and subtyping have fairly strict requirements on tuple matching,
// requiring element names to either match up or be disjoint.
if (kind < ConstraintKind::Conversion) {
if (tuple1->getNumElements() != tuple2->getNumElements())
return getTypeMatchFailure(locator);
for (unsigned i = 0, n = tuple1->getNumElements(); i != n; ++i) {
const auto &elt1 = tuple1->getElement(i);
const auto &elt2 = tuple2->getElement(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 <= ConstraintKind::Equal)
return getTypeMatchFailure(locator);
// For subtyping constraints, just make sure that this name isn't
// used at some other position.
if (elt2.hasName() && tuple1->getNamedElementId(elt2.getName()) != -1)
return getTypeMatchFailure(locator);
}
// Variadic bit must match.
if (elt1.isVararg() != elt2.isVararg())
return getTypeMatchFailure(locator);
// Compare the element types.
auto result = matchTypes(elt1.getType(), elt2.getType(), kind, subflags,
locator.withPathElement(
LocatorPathElt::getTupleElement(i)));
if (result.isFailure())
return result;
}
return getTypeMatchSuccess();
}
assert(kind >= ConstraintKind::Conversion);
ConstraintKind subKind;
switch (kind) {
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ArgumentConversion:
case ConstraintKind::Conversion:
subKind = ConstraintKind::Conversion;
break;
case ConstraintKind::Bind:
case ConstraintKind::BindParam:
case ConstraintKind::BindToPointerType:
case ConstraintKind::Equal:
case ConstraintKind::Subtype:
case ConstraintKind::ApplicableFunction:
case ConstraintKind::DynamicCallableApplicableFunction:
case ConstraintKind::BindOverload:
case ConstraintKind::CheckedCast:
case ConstraintKind::ConformsTo:
case ConstraintKind::Defaultable:
case ConstraintKind::Disjunction:
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::BridgingConversion:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
llvm_unreachable("Not a conversion");
}
// Compute the element shuffles for conversions.
SmallVector<unsigned, 16> sources;
if (computeTupleShuffle(tuple1, tuple2, sources))
return getTypeMatchFailure(locator);
// Check each of the elements.
for (unsigned idx2 = 0, n = sources.size(); idx2 != n; ++idx2) {
unsigned idx1 = sources[idx2];
// Match up the types.
const auto &elt1 = tuple1->getElement(idx1);
const auto &elt2 = tuple2->getElement(idx2);
auto result = matchTypes(elt1.getType(), elt2.getType(), subKind, subflags,
locator.withPathElement(
LocatorPathElt::getTupleElement(idx1)));
if (result.isFailure())
return result;
}
return getTypeMatchSuccess();
}
// Returns 'false' (i.e. no error) if it is legal to match functions with the
// corresponding function type representations and the given match kind.
static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
FunctionTypeRepresentation rep2,
ConstraintKind kind) {
switch (kind) {
case ConstraintKind::Bind:
case ConstraintKind::BindParam:
case ConstraintKind::BindToPointerType:
case ConstraintKind::Equal:
return rep1 != rep2;
case ConstraintKind::Subtype:
case ConstraintKind::Conversion:
case ConstraintKind::BridgingConversion:
case ConstraintKind::ArgumentConversion:
case ConstraintKind::OperatorArgumentConversion:
case ConstraintKind::ApplicableFunction:
case ConstraintKind::DynamicCallableApplicableFunction:
case ConstraintKind::BindOverload:
case ConstraintKind::CheckedCast:
case ConstraintKind::ConformsTo:
case ConstraintKind::Defaultable:
case ConstraintKind::Disjunction:
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
return false;
}
llvm_unreachable("Unhandled ConstraintKind in switch.");
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
ConstraintKind kind, TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
// A non-throwing function can be a subtype of a throwing function.
if (func1->throws() != func2->throws()) {
// Cannot drop 'throws'.
if (func1->throws() || kind < ConstraintKind::Subtype)
return getTypeMatchFailure(locator);
}
// A non-@noescape function type can be a subtype of a @noescape function
// type.
if (func1->isNoEscape() != func2->isNoEscape() &&
(func1->isNoEscape() || kind < ConstraintKind::Subtype))
return getTypeMatchFailure(locator);
if (matchFunctionRepresentations(func1->getExtInfo().getRepresentation(),
func2->getExtInfo().getRepresentation(),
kind)) {
return getTypeMatchFailure(locator);
}
// Determine how we match up the input/result types.
ConstraintKind subKind;
switch (kind) {
case ConstraintKind::Bind:
case ConstraintKind::BindParam:
case ConstraintKind::BindToPointerType:
case ConstraintKind::Equal:
subKind = kind;
break;
case ConstraintKind::Subtype:
case ConstraintKind::Conversion:
case ConstraintKind::ArgumentConversion:
case ConstraintKind::OperatorArgumentConversion:
subKind = ConstraintKind::Subtype;
break;
case ConstraintKind::ApplicableFunction:
case ConstraintKind::DynamicCallableApplicableFunction:
case ConstraintKind::BindOverload:
case ConstraintKind::CheckedCast:
case ConstraintKind::ConformsTo:
case ConstraintKind::Defaultable:
case ConstraintKind::Disjunction:
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::BridgingConversion:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
llvm_unreachable("Not a relational constraint");
}
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
SmallVector<AnyFunctionType::Param, 8> func1Params;
func1Params.append(func1->getParams().begin(), func1->getParams().end());
SmallVector<AnyFunctionType::Param, 8> func2Params;
func2Params.append(func2->getParams().begin(), func2->getParams().end());
// Add a very narrow exception to SE-0110 by allowing functions that
// take multiple arguments to be passed as an argument in places
// that expect a function that takes a single tuple (of the same
// arity).
auto isSingleParam = [&](ArrayRef<AnyFunctionType::Param> params) {
return (params.size() == 1 &&
params[0].getLabel().empty() &&
!params[0].isVariadic());
};
auto canImplodeParams = [&](ArrayRef<AnyFunctionType::Param> params) {
if (params.size() == 1)
return false;
for (auto param : params)
if (param.isVariadic() || param.isInOut())
return false;
return true;
};
auto implodeParams = [&](SmallVectorImpl<AnyFunctionType::Param> &params) {
auto input = AnyFunctionType::composeInput(getASTContext(), params,
/*canonicalVararg=*/false);
params.clear();
params.emplace_back(input);
};
{
SmallVector<LocatorPathElt, 4> path;
locator.getLocatorParts(path);
// Find the last path element, skipping OptionalPayload elements
// so that we allow this exception in cases of optional injection.
auto last = std::find_if(
path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
return elt.getKind() != ConstraintLocator::OptionalPayload;
});
if (last != path.rend()) {
if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
if (isSingleParam(func2Params) &&
canImplodeParams(func1Params)) {
implodeParams(func1Params);
} else if (!getASTContext().isSwiftVersionAtLeast(5) &&
isSingleParam(func1Params) &&
canImplodeParams(func2Params)) {
auto *simplified = locator.trySimplifyToExpr();
// We somehow let tuple unsplatting function conversions
// through in some cases in Swift 4, so let's let that
// continue to work, but only for Swift 4.
if (simplified && isa<DeclRefExpr>(simplified)) {
implodeParams(func2Params);
}
}
}
}
}
// https://bugs.swift.org/browse/SR-6796
// Add a super-narrow hack to allow:
// (()) -> T to be passed in place of () -> T
if (getASTContext().isSwiftVersionAtLeast(4) &&
!getASTContext().isSwiftVersionAtLeast(5)) {
SmallVector<LocatorPathElt, 4> path;
locator.getLocatorParts(path);
// Find the last path element, skipping GenericArgument elements
// so that we allow this exception in cases of optional types, and
// skipping OptionalPayload elements so that we allow this
// exception in cases of optional injection.
auto last = std::find_if(
path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
return elt.getKind() != ConstraintLocator::GenericArgument &&
elt.getKind() != ConstraintLocator::OptionalPayload;
});
if (last != path.rend()) {
if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
if (isSingleParam(func1Params) &&
func1Params[0].getOldType()->isVoid()) {
if (func2Params.empty()) {
func2Params.emplace_back(getASTContext().TheEmptyTupleType);
}
}
}
}
}
// Input types can be contravariant (or equal).
auto argumentLocator = locator.withPathElement(
ConstraintLocator::FunctionArgument);
if (func1Params.size() != func2Params.size())
return getTypeMatchFailure(argumentLocator);
for (unsigned i : indices(func1Params)) {
auto func1Param = func1Params[i];
auto func2Param = func2Params[i];
// Variadic bit must match.
if (func1Param.isVariadic() != func2Param.isVariadic())
return getTypeMatchFailure(argumentLocator);
// Labels must match.
//
// FIXME: We should not end up with labels here at all, but we do
// from invalid code in diagnostics, and as a result of code completion
// directly building constraint systems.
if (func1Param.getLabel() != func2Param.getLabel())
return getTypeMatchFailure(argumentLocator);
// FIXME: We should check value ownership too, but it's not completely
// trivial because of inout-to-pointer conversions.
// Compare the parameter types.
auto result = matchTypes(func2Param.getOldType(),
func1Param.getOldType(),
subKind, subflags,
(func1Params.size() == 1
? argumentLocator
: argumentLocator.withPathElement(
LocatorPathElt::getTupleElement(i))));
if (result.isFailure())
return result;
}
// Result type can be covariant (or equal).
return matchTypes(func1->getResult(), func2->getResult(), subKind,
subflags,
locator.withPathElement(
ConstraintLocator::FunctionResult));
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchSuperclassTypes(Type type1, Type type2,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
auto classDecl2 = type2->getClassOrBoundGenericClass();
for (auto super1 = type1->getSuperclass();
super1;
super1 = super1->getSuperclass()) {
if (super1->getClassOrBoundGenericClass() != classDecl2)
continue;
return matchTypes(super1, type2, ConstraintKind::Bind,
subflags, locator);
}
return getTypeMatchFailure(locator);
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
ConstraintLocatorBuilder locator) {
TypeMatchOptions subflags = TMF_GenerateConstraints;
// 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 getTypeMatchSuccess();
// Match up the parents, exactly.
return matchTypes(nominal1->getParent(), nominal2->getParent(),
ConstraintKind::Bind, subflags,
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()) {
auto result = matchTypes(bound1->getParent(), bound2->getParent(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::ParentType));
if (result.isFailure())
return result;
}
// Match up the generic arguments, exactly.
auto args1 = bound1->getGenericArgs();
auto args2 = bound2->getGenericArgs();
if (args1.size() != args2.size()) {
return getTypeMatchFailure(locator);
}
for (unsigned i = 0, n = args1.size(); i != n; ++i) {
auto result = matchTypes(args1[i], args2[i], ConstraintKind::Bind,
subflags, locator.withPathElement(
LocatorPathElt::getGenericArgument(i)));
if (result.isFailure())
return result;
}
return getTypeMatchSuccess();
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
ConstraintKind kind,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
// If the first type is a type variable or member thereof, there's nothing
// we can do now.
if (type1->isTypeVariableOrMember()) {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::create(*this, kind, type1, type2,
getConstraintLocator(locator)));
return getTypeMatchSuccess();
}
return getTypeMatchAmbiguous();
}
// FIXME: Feels like a hack.
if (type1->is<InOutType>())
return getTypeMatchFailure(locator);
// FIXME; Feels like a hack...nothing actually "conforms" here, and
// we need to disallow conversions from @noescape functions to Any.
// Conformance to 'Any' always holds.
if (type2->isAny()) {
auto *fnTy = type1->getAs<FunctionType>();
if (!fnTy || !fnTy->isNoEscape())
return getTypeMatchSuccess();
if (shouldAttemptFixes()) {
auto &ctx = getASTContext();
auto *fix = MarkExplicitlyEscaping::create(
*this, getConstraintLocator(locator), ctx.TheAnyType);
if (!recordFix(fix))
return getTypeMatchSuccess();
}
return getTypeMatchFailure(locator);
}
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
// Handle existential metatypes.
if (auto meta1 = type1->getAs<MetatypeType>()) {
if (auto meta2 = type2->getAs<ExistentialMetatypeType>()) {
return matchExistentialTypes(meta1->getInstanceType(),
meta2->getInstanceType(), kind, subflags,
locator.withPathElement(
ConstraintLocator::InstanceType));
}
}
if (!type2->isExistentialType())
return getTypeMatchFailure(locator);
auto layout = type2->getExistentialLayout();
if (auto layoutConstraint = layout.getLayoutConstraint()) {
if (layoutConstraint->isClass()) {
if (kind == ConstraintKind::ConformsTo) {
if (!type1->satisfiesClassConstraint())
return getTypeMatchFailure(locator);
} else {
// Subtype relation to AnyObject also allows class-bound
// existentials that are not @objc and therefore carry
// witness tables.
if (!type1->isClassExistentialType() &&
!type1->mayHaveSuperclass())
return getTypeMatchFailure(locator);
}
// Keep going.
}
}
if (layout.explicitSuperclass) {
auto subKind = std::min(ConstraintKind::Subtype, kind);
auto result = matchTypes(type1, layout.explicitSuperclass, subKind,
subflags, locator);
if (result.isFailure())
return result;
}
for (auto *proto : layout.getProtocols()) {
auto *protoDecl = proto->getDecl();
if (auto superclass = protoDecl->getSuperclass()) {
auto subKind = std::min(ConstraintKind::Subtype, kind);
auto result = matchTypes(type1, superclass, subKind,
subflags, locator);
if (result.isFailure())
return result;
}
switch (simplifyConformsToConstraint(type1, protoDecl, kind, locator,
subflags)) {
case SolutionKind::Solved:
case SolutionKind::Unsolved:
break;
case SolutionKind::Error:
return getTypeMatchFailure(locator);
}
}
return getTypeMatchSuccess();
}
static bool isStringCompatiblePointerBaseType(TypeChecker &TC,
DeclContext *DC,
Type baseType) {
// Allow strings to be passed to pointer-to-byte or pointer-to-void types.
if (baseType->isEqual(TC.getInt8Type(DC)))
return true;
if (baseType->isEqual(TC.getUInt8Type(DC)))
return true;
if (baseType->isEqual(TC.Context.TheEmptyTupleType))
return true;
return false;
}
/// Determine whether the first type with the given number of optionals
/// is potentially more optional than the second type with its number of
/// optionals.
static bool isPotentiallyMoreOptionalThan(Type type1, Type type2) {
SmallVector<Type, 2> optionals1;
Type objType1 = type1->lookThroughAllOptionalTypes(optionals1);
auto numOptionals1 = optionals1.size();
SmallVector<Type, 2> optionals2;
type2->lookThroughAllOptionalTypes(optionals2);
auto numOptionals2 = optionals2.size();
if (numOptionals1 <= numOptionals2 && !objType1->isTypeVariableOrMember())
return false;
return true;
}
/// Enumerate all of the applicable optional conversion restrictions
static void enumerateOptionalConversionRestrictions(
Type type1, Type type2,
ConstraintKind kind, ConstraintLocatorBuilder locator,
llvm::function_ref<void(ConversionRestrictionKind)> fn) {
// Optional-to-optional.
if (type1->getOptionalObjectType() && type2->getOptionalObjectType())
fn(ConversionRestrictionKind::OptionalToOptional);
// Inject a value into an optional.
if (isPotentiallyMoreOptionalThan(type2, type1)) {
fn(ConversionRestrictionKind::ValueToOptional);
}
}
/// Determine whether we can bind the given type variable to the given
/// fixed type.
static bool isBindable(TypeVariableType *typeVar, Type type) {
return !ConstraintSystem::typeVarOccursInType(typeVar, type) &&
!type->is<DependentMemberType>();
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchTypesBindTypeVar(
TypeVariableType *typeVar, Type type, ConstraintKind kind,
TypeMatchOptions flags, ConstraintLocatorBuilder locator,
llvm::function_ref<TypeMatchResult()> formUnsolvedResult) {
assert(typeVar->is<TypeVariableType>() && "Expected a type variable!");
// FIXME: Due to some SE-0110 related code farther up we can end
// up with type variables wrapped in parens that will trip this
// assert. For now, maintain the existing behavior.
// assert(!type->is<TypeVariableType>() && "Expected a non-type variable!");
// Simplify the right-hand type and perform the "occurs" check.
typeVar = getRepresentative(typeVar);
type = simplifyType(type, flags);
if (!isBindable(typeVar, type))
return formUnsolvedResult();
// Since member lookup doesn't check requirements
// it might sometimes return types which are not
// visible in the current context e.g. typealias
// defined in constrained extension, substitution
// of which might produce error type for base, so
// assignement should thead lightly and just fail
// if it encounters such types.
if (type->hasError())
return getTypeMatchFailure(locator);
// Equal constraints allow mixed LValue/RValue bindings, but
// if we bind a type to a type variable that can bind to
// LValues as part of simplifying the Equal constraint we may
// later block a binding of the opposite "LValue-ness" to the
// same type variable that happens as part of simplifying
// another constraint.
if (kind == ConstraintKind::Equal) {
if (typeVar->getImpl().canBindToLValue())
return formUnsolvedResult();
type = type->getRValueType();
}
// If the left-hand type variable cannot bind to an lvalue,
// but we still have an lvalue, fail.
if (!typeVar->getImpl().canBindToLValue() && type->hasLValueType()) {
return getTypeMatchFailure(locator);
}
// If the left-hand type variable cannot bind to an inout,
// but we still have an inout, fail.
if (!typeVar->getImpl().canBindToInOut() && type->is<InOutType>()) {
return getTypeMatchFailure(locator);
}
// Disallow bindings of noescape functions to type variables that
// represent an opened archetype. If we allowed this it would allow
// the noescape function to potentially escape.
if (auto *fnTy = type->getAs<FunctionType>()) {
if (fnTy->isNoEscape() && typeVar->getImpl().getGenericParameter()) {
if (shouldAttemptFixes()) {
auto *fix = MarkExplicitlyEscaping::create(
*this, getConstraintLocator(locator));
if (recordFix(fix))
return getTypeMatchFailure(locator);
// Allow no-escape function to be bound with recorded fix.
} else {
return getTypeMatchFailure(locator);
}
}
}
// Okay. Bind below.
// A constraint that binds any pointer to a void pointer is
// ineffective, since any pointer can be converted to a void pointer.
if (kind == ConstraintKind::BindToPointerType && type->isVoid()) {
// Bind type1 to Void only as a last resort.
addConstraint(ConstraintKind::Defaultable, typeVar, type,
getConstraintLocator(locator));
return getTypeMatchSuccess();
}
if (!typeVar->getImpl().canBindToLValue()) {
// When binding a fixed type to a type variable that cannot contain
// lvalues, any type variables within the fixed type cannot contain
// lvalues either.
type.visit([&](Type t) {
if (auto *tvt = dyn_cast<TypeVariableType>(t.getPointer()))
typeVar->getImpl().setCannotBindToLValue(getSavedBindings());
});
}
assignFixedType(typeVar, type);
return getTypeMatchSuccess();
}
static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
Type type2, Expr *anchor,
LocatorPathElt &req) {
// Can't fix not yet properly resolved types.
if (type1->hasTypeVariable() || type2->hasTypeVariable())
return nullptr;
// If dependent members are present here it's because
// base doesn't conform to associated type's protocol.
if (type1->hasDependentMember() || type2->hasDependentMember())
return nullptr;
// Build simplified locator which only contains anchor and requirement info.
ConstraintLocatorBuilder requirement(cs.getConstraintLocator(anchor));
auto *reqLoc = cs.getConstraintLocator(requirement.withPathElement(req));
auto reqKind = static_cast<RequirementKind>(req.getValue2());
switch (reqKind) {
case RequirementKind::SameType: {
return SkipSameTypeRequirement::create(cs, type1, type2, reqLoc);
}
case RequirementKind::Superclass: {
return SkipSuperclassRequirement::create(cs, type1, type2, reqLoc);
}
case RequirementKind::Conformance:
case RequirementKind::Layout:
llvm_unreachable("conformance requirements are handled elsewhere");
}
}
static void
repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
ConstraintLocatorBuilder locator) {
SmallVector<LocatorPathElt, 4> path;
auto *anchor = locator.getLocatorParts(path);
if (path.empty())
return;
auto &elt = path.back();
switch (elt.getKind()) {
case ConstraintLocator::TypeParameterRequirement: {
if (auto *fix = fixRequirementFailure(cs, lhs, rhs, anchor, elt))
conversionsOrFixes.push_back(fix);
break;
}
case ConstraintLocator::ClosureResult: {
auto *fix = ContextualMismatch::create(cs, lhs, rhs,
cs.getConstraintLocator(locator));
conversionsOrFixes.push_back(fix);
break;
}
default:
return;
}
}
ConstraintSystem::TypeMatchResult
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
// If we have type variables that have been bound to fixed types, look through
// to the fixed type.
type1 = getFixedTypeRecursive(type1, flags, kind == ConstraintKind::Equal);
type2 = getFixedTypeRecursive(type2, flags, kind == ConstraintKind::Equal);
auto desugar1 = type1->getDesugaredType();
auto desugar2 = type2->getDesugaredType();
// If the types are obviously equivalent, we're done.
if (desugar1->isEqual(desugar2))
return getTypeMatchSuccess();
// Local function that should be used to produce the return value whenever
// this function was unable to resolve the constraint. It should be used
// within \c matchTypes() as
//
// return formUnsolvedResult();
//
// along any unsolved path. No other returns should produce
// SolutionKind::Unsolved or inspect TMF_GenerateConstraints.
auto formUnsolvedResult = [&] {
// If we're supposed to generate constraints (i.e., this is a
// newly-generated constraint), do so now.
if (flags.contains(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.
addUnsolvedConstraint(Constraint::create(*this, kind, type1, type2,
getConstraintLocator(locator)));
return getTypeMatchSuccess();
}
return getTypeMatchAmbiguous();
};
auto *typeVar1 = dyn_cast<TypeVariableType>(desugar1);
auto *typeVar2 = dyn_cast<TypeVariableType>(desugar2);
// If either (or both) types are type variables, unify the type variables.
if (typeVar1 || typeVar2) {
// Handle the easy case of both being type variables, and being
// identical, first.
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 getTypeMatchSuccess();
}
}
switch (kind) {
case ConstraintKind::Bind:
case ConstraintKind::BindToPointerType:
case ConstraintKind::Equal: {
if (typeVar1 && typeVar2) {
auto rep1 = getRepresentative(typeVar1);
auto rep2 = getRepresentative(typeVar2);
// If exactly one of the type variables can bind to an lvalue, we
// can't merge these two type variables.
if (kind == ConstraintKind::Equal &&
rep1->getImpl().canBindToLValue()
!= rep2->getImpl().canBindToLValue())
return formUnsolvedResult();
// Merge the equivalence classes corresponding to these two variables.
mergeEquivalenceClasses(rep1, rep2);
return getTypeMatchSuccess();
}
assert((type1->is<TypeVariableType>() || type2->is<TypeVariableType>()) &&
"Expected a type variable!");
// FIXME: Due to some SE-0110 related code farther up we can end
// up with type variables wrapped in parens that will trip this
// assert. For now, maintain the existing behavior.
// assert(
// (!type1->is<TypeVariableType>() || !type2->is<TypeVariableType>())
// && "Expected a non-type variable!");
auto *typeVar = typeVar1 ? typeVar1 : typeVar2;
auto type = typeVar1 ? type2 : type1;
return matchTypesBindTypeVar(typeVar, type, kind, flags, locator,
formUnsolvedResult);
}
case ConstraintKind::BindParam: {
if (typeVar2 && !typeVar1) {
// Simplify the left-hand type and perform the "occurs" check.
auto rep2 = getRepresentative(typeVar2);
type1 = simplifyType(type1, flags);
if (!isBindable(typeVar2, type1))
return formUnsolvedResult();
if (auto *iot = type1->getAs<InOutType>()) {
if (!rep2->getImpl().canBindToLValue())
return getTypeMatchFailure(locator);
assignFixedType(rep2, LValueType::get(iot->getObjectType()));
} else {
assignFixedType(rep2, type1);
}
return getTypeMatchSuccess();
} else if (typeVar1 && !typeVar2) {
// Simplify the right-hand type and perform the "occurs" check.
auto rep1 = getRepresentative(typeVar1);
type2 = simplifyType(type2, flags);
if (!isBindable(rep1, type2))
return formUnsolvedResult();
if (auto *lvt = type2->getAs<LValueType>()) {
if (!rep1->getImpl().canBindToInOut())
return getTypeMatchFailure(locator);
assignFixedType(rep1, InOutType::get(lvt->getObjectType()));
} else {
assignFixedType(rep1, type2);
}
return getTypeMatchSuccess();
} if (typeVar1 && typeVar2) {
auto rep1 = getRepresentative(typeVar1);
auto rep2 = getRepresentative(typeVar2);
if (!rep1->getImpl().canBindToInOut() ||
!rep2->getImpl().canBindToLValue()) {
// Merge the equivalence classes corresponding to these two variables.
mergeEquivalenceClasses(rep1, rep2);
return getTypeMatchSuccess();
}
}
return formUnsolvedResult();
}
case ConstraintKind::Subtype:
case ConstraintKind::Conversion:
case ConstraintKind::ArgumentConversion:
case ConstraintKind::OperatorArgumentConversion:
return formUnsolvedResult();
case ConstraintKind::ApplicableFunction:
case ConstraintKind::DynamicCallableApplicableFunction:
case ConstraintKind::BindOverload:
case ConstraintKind::BridgingConversion:
case ConstraintKind::CheckedCast:
case ConstraintKind::ConformsTo:
case ConstraintKind::Defaultable:
case ConstraintKind::Disjunction:
case ConstraintKind::DynamicTypeOf:
case ConstraintKind::EscapableFunctionOf:
case ConstraintKind::OpenedExistentialOf:
case ConstraintKind::KeyPath:
case ConstraintKind::KeyPathApplication:
case ConstraintKind::LiteralConformsTo:
case ConstraintKind::OptionalObject:
case ConstraintKind::SelfObjectOfProtocol:
case ConstraintKind::UnresolvedValueMember:
case ConstraintKind::ValueMember:
case ConstraintKind::FunctionInput:
case ConstraintKind::FunctionResult:
llvm_unreachable("Not a relational constraint");
}
}
// If one of the types is a member type of a type variable type,
// there's nothing we can do.
if (desugar1->isTypeVariableOrMember() ||
desugar2->isTypeVariableOrMember()) {
return formUnsolvedResult();
}
llvm::SmallVector<RestrictionOrFix, 4> conversionsOrFixes;
// Decompose parallel structure.
TypeMatchOptions subflags =
getDefaultDecompositionOptions(flags) - 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::Error:
case TypeKind::Unresolved:
return getTypeMatchFailure(locator);
case TypeKind::GenericTypeParam:
llvm_unreachable("unmapped dependent type in type checker");
case TypeKind::TypeVariable:
llvm_unreachable("type variables should have already been handled by now");
case TypeKind::DependentMember:
// Nothing we can solve.
return formUnsolvedResult();
case TypeKind::Module:
case TypeKind::PrimaryArchetype:
case TypeKind::OpenedArchetype:
case TypeKind::NestedArchetype:
// If two module types or archetypes were not already equal, there's
// nothing more we can do.
return getTypeMatchFailure(locator);
case TypeKind::Tuple: {
auto result = matchTupleTypes(cast<TupleType>(desugar1),
cast<TupleType>(desugar2),
kind, subflags, locator);
if (result != SolutionKind::Error)
return result;
// FIXME: All cases in this switch should go down to the fix logic
// to give repairFailures() a chance to run, but this breaks stuff
// right now.
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);
// Check for CF <-> ObjectiveC bridging.
if (isa<ClassType>(desugar1) &&
kind >= ConstraintKind::Subtype) {
auto class1 = cast<ClassDecl>(nominal1->getDecl());
auto class2 = cast<ClassDecl>(nominal2->getDecl());
// CF -> Objective-C via toll-free bridging.
if (class1->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
class2->getForeignClassKind() != ClassDecl::ForeignKind::CFType &&
class1->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::CFTollFreeBridgeToObjC);
}
// Objective-C -> CF via toll-free bridging.
if (class2->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
class1->getForeignClassKind() != ClassDecl::ForeignKind::CFType &&
class2->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ObjCTollFreeBridgeToCF);
}
}
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);
// A.Type < B.Type if A < B and both A and B are classes.
// P.Type < Q.Type if P < Q, both P and Q are protocols, and P.Type
// and Q.Type are both existential metatypes
auto subKind = std::min(kind, ConstraintKind::Subtype);
// If instance types can't have a subtype relationship
// it means that such types can be simply equated.
auto instanceType1 = meta1->getInstanceType();
auto instanceType2 = meta2->getInstanceType();
if (isa<MetatypeType>(meta1) &&
!(instanceType1->mayHaveSuperclass() &&
instanceType2->getClassOrBoundGenericClass())) {
subKind = ConstraintKind::Bind;
}
return matchTypes(
instanceType1, instanceType2, subKind, subflags,
locator.withPathElement(ConstraintLocator::InstanceType));
}
case TypeKind::Function: {
auto func1 = cast<FunctionType>(desugar1);
auto func2 = cast<FunctionType>(desugar2);
return matchFunctionTypes(func1, func2, kind, flags, locator);
}
case TypeKind::GenericFunction:
llvm_unreachable("Polymorphic function type should have been opened");
case TypeKind::ProtocolComposition:
// Existential types handled below.
break;
case TypeKind::LValue:
if (kind == ConstraintKind::BindParam)
return getTypeMatchFailure(locator);
return matchTypes(cast<LValueType>(desugar1)->getObjectType(),
cast<LValueType>(desugar2)->getObjectType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::LValueConversion));
case TypeKind::InOut:
// If the RHS is an inout type, the LHS must be an @lvalue type.
if (kind == ConstraintKind::BindParam ||
kind >= ConstraintKind::OperatorArgumentConversion)
return getTypeMatchFailure(locator);
return matchTypes(cast<InOutType>(desugar1)->getObjectType(),
cast<InOutType>(desugar2)->getObjectType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(ConstraintLocator::LValueConversion));
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 (kind >= ConstraintKind::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). Note that we cannot get
// a value of inout type as an lvalue though.
if (type1->is<LValueType>() && !type2->is<InOutType>()) {
return matchTypes(type1->getRValueType(), type2,
kind, subflags, locator);
}
}
if (kind >= ConstraintKind::Subtype) {
// Subclass-to-superclass conversion.
if (type1->mayHaveSuperclass() &&
type2->getClassOrBoundGenericClass() &&
type1->getClassOrBoundGenericClass()
!= type2->getClassOrBoundGenericClass()) {
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
}
// Existential-to-superclass conversion.
if (type1->isClassExistentialType() &&
type2->getClassOrBoundGenericClass()) {
conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
}
// Metatype-to-existential-metatype conversion.
//
// Equivalent to a conformance relation on the instance types.
if (type1->is<MetatypeType>() &&
type2->is<ExistentialMetatypeType>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::MetatypeToExistentialMetatype);
}
// Existential-metatype-to-superclass-metatype conversion.
if (type2->is<MetatypeType>()) {
if (auto *meta1 = type1->getAs<ExistentialMetatypeType>()) {
if (meta1->getInstanceType()->isClassExistentialType()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ExistentialMetatypeToMetatype);
}
}
}
// Concrete value to existential conversion.
if (!type1->is<LValueType>() &&
type2->isExistentialType()) {
// Penalize conversions to Any, and disallow conversions of
// noescape functions to Any.
if (kind >= ConstraintKind::Conversion && type2->isAny()) {
if (auto *fnTy = type1->getAs<FunctionType>()) {
if (fnTy->isNoEscape()) {
if (shouldAttemptFixes()) {
auto &ctx = getASTContext();
auto *fix = MarkExplicitlyEscaping::create(
*this, getConstraintLocator(locator), ctx.TheAnyType);
if (recordFix(fix))
return getTypeMatchFailure(locator);
// Allow 'no-escape' functions to be converted to 'Any'
// with a recorded fix that helps us to properly diagnose
// such situations.
} else {
return getTypeMatchFailure(locator);
}
}
}
increaseScore(ScoreKind::SK_EmptyExistentialConversion);
}
conversionsOrFixes.push_back(ConversionRestrictionKind::Existential);
}
// T -> AnyHashable.
if (isAnyHashableType(desugar2)) {
// Don't allow this in operator contexts or we'll end up allowing
// 'T() == U()' for unrelated T and U that just happen to be Hashable.
// We can remove this special case when we implement operator hiding.
if (!type1->is<LValueType>() &&
kind != ConstraintKind::OperatorArgumentConversion) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(
ConversionRestrictionKind::HashableToAnyHashable);
}
}
// Metatype to object conversion.
//
// Class and protocol metatypes are interoperable with certain Objective-C
// runtime classes, but only when ObjC interop is enabled.
if (TC.getLangOpts().EnableObjCInterop) {
// These conversions are between concrete types that don't need further
// resolution, so we can consider them immediately solved.
auto addSolvedRestrictedConstraint
= [&](ConversionRestrictionKind restriction) -> TypeMatchResult {
addRestrictedConstraint(ConstraintKind::Subtype, restriction,
type1, type2, locator);
return getTypeMatchSuccess();
};
if (auto meta1 = type1->getAs<MetatypeType>()) {
if (meta1->getInstanceType()->mayHaveSuperclass()
&& type2->isAnyObject()) {
increaseScore(ScoreKind::SK_UserConversion);
return addSolvedRestrictedConstraint(
ConversionRestrictionKind::ClassMetatypeToAnyObject);
}
// Single @objc protocol value metatypes can be converted to the ObjC
// Protocol class type.
auto isProtocolClassType = [&](Type t) -> bool {
if (auto classDecl = t->getClassOrBoundGenericClass())
if (classDecl->getName() == getASTContext().Id_Protocol
&& classDecl->getModuleContext()->getName()
== getASTContext().Id_ObjectiveC)
return true;
return false;
};
if (auto protoTy = meta1->getInstanceType()->getAs<ProtocolType>()) {
if (protoTy->getDecl()->isObjC()
&& isProtocolClassType(type2)) {
increaseScore(ScoreKind::SK_UserConversion);
return addSolvedRestrictedConstraint(
ConversionRestrictionKind::ProtocolMetatypeToProtocolClass);
}
}
}
if (auto meta1 = type1->getAs<ExistentialMetatypeType>()) {
// Class-constrained existential metatypes can be converted to AnyObject.
if (meta1->getInstanceType()->isClassExistentialType()
&& type2->isAnyObject()) {
increaseScore(ScoreKind::SK_UserConversion);
return addSolvedRestrictedConstraint(
ConversionRestrictionKind::ExistentialMetatypeToAnyObject);
}
}
}
// Special implicit nominal conversions.
if (!type1->is<LValueType>() && kind >= ConstraintKind::Subtype) {
// Array -> Array.
if (isArrayType(desugar1) && isArrayType(desugar2)) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
// Dictionary -> Dictionary.
} else if (isDictionaryType(desugar1) && isDictionaryType(desugar2)) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(
ConversionRestrictionKind::DictionaryUpcast);
// Set -> Set.
} else if (isSetType(desugar1) && isSetType(desugar2)) {
assert(!type2->is<LValueType>() && "Unexpected lvalue type!");
conversionsOrFixes.push_back(
ConversionRestrictionKind::SetUpcast);
}
}
}
if (kind == ConstraintKind::BindToPointerType) {
if (desugar2->isEqual(getASTContext().TheEmptyTupleType))
return getTypeMatchSuccess();
}
if (kind >= ConstraintKind::Conversion) {
// It is never legal to form an autoclosure that results in these
// implicit conversions to pointer types.
bool isAutoClosureArgument = false;
if (auto last = locator.last())
if (last->getKind() == ConstraintLocator::AutoclosureResult)
isAutoClosureArgument = true;
// Pointer arguments can be converted from pointer-compatible types.
if (kind >= ConstraintKind::ArgumentConversion) {
Type unwrappedType2 = type2;
bool type2IsOptional = false;
if (Type unwrapped = type2->getOptionalObjectType()) {
type2IsOptional = true;
unwrappedType2 = unwrapped;
}
PointerTypeKind pointerKind;
if (Type pointeeTy =
unwrappedType2->getAnyPointerElementType(pointerKind)) {
switch (pointerKind) {
case PTK_UnsafeRawPointer:
case PTK_UnsafeMutableRawPointer:
case PTK_UnsafePointer:
case PTK_UnsafeMutablePointer:
// UnsafeMutablePointer can be converted from an inout reference to a
// scalar or array.
if (!isAutoClosureArgument) {
if (auto inoutType1 = dyn_cast<InOutType>(desugar1)) {
auto inoutBaseType = inoutType1->getInOutObjectType();
Type simplifiedInoutBaseType = getFixedTypeRecursive(
inoutBaseType, /*wantRValue=*/true);
// FIXME: If the base is still a type variable, we can't tell
// what to do here. Might have to try \c ArrayToPointer and make
// it more robust.
if (isArrayType(simplifiedInoutBaseType)) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
}
conversionsOrFixes.push_back(
ConversionRestrictionKind::InoutToPointer);
}
}
// Operators cannot use these implicit conversions.
if (kind == ConstraintKind::ArgumentConversion) {
// We can potentially convert from an UnsafeMutablePointer
// of a different type, if we're a void pointer.
Type unwrappedType1 = type1;
bool type1IsOptional = false;
if (Type unwrapped = type1->getOptionalObjectType()) {
type1IsOptional = true;
unwrappedType1 = unwrapped;
}
// Don't handle normal optional-related conversions here.
if (unwrappedType1->isEqual(unwrappedType2))
break;
PointerTypeKind type1PointerKind;
bool type1IsPointer{
unwrappedType1->getAnyPointerElementType(type1PointerKind)};
bool optionalityMatches = !type1IsOptional || type2IsOptional;
if (type1IsPointer && optionalityMatches) {
if (type1PointerKind == PTK_UnsafeMutablePointer) {
// Favor an UnsafeMutablePointer-to-UnsafeMutablePointer
// conversion.
if (type1PointerKind != pointerKind)
increaseScore(ScoreKind::SK_ValueToPointerConversion);
conversionsOrFixes.push_back(
ConversionRestrictionKind::PointerToPointer);
}
// UnsafeMutableRawPointer -> UnsafeRawPointer
else if (type1PointerKind == PTK_UnsafeMutableRawPointer &&
pointerKind == PTK_UnsafeRawPointer) {
if (type1PointerKind != pointerKind)
increaseScore(ScoreKind::SK_ValueToPointerConversion);
conversionsOrFixes.push_back(
ConversionRestrictionKind::PointerToPointer);
}
}
// UnsafePointer and UnsafeRawPointer can also be converted from an
// array or string value, or a UnsafePointer or
// AutoreleasingUnsafeMutablePointer.
if (pointerKind == PTK_UnsafePointer
|| pointerKind == PTK_UnsafeRawPointer) {
if (!isAutoClosureArgument) {
if (isArrayType(type1)) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::ArrayToPointer);
}
// The pointer can be converted from a string, if the element
// type is compatible.
if (type1->isEqual(TC.getStringType(DC))) {
auto baseTy = getFixedTypeRecursive(pointeeTy, false);
if (baseTy->isTypeVariableOrMember() ||
isStringCompatiblePointerBaseType(TC, DC, baseTy))
conversionsOrFixes.push_back(
ConversionRestrictionKind::StringToPointer);
}
}
if (type1IsPointer && optionalityMatches &&
(type1PointerKind == PTK_UnsafePointer ||
type1PointerKind == PTK_AutoreleasingUnsafeMutablePointer)) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::PointerToPointer);
}
}
}
break;
case PTK_AutoreleasingUnsafeMutablePointer:
// PTK_AutoreleasingUnsafeMutablePointer can be converted from an
// inout reference to a scalar.
if (!isAutoClosureArgument && type1->is<InOutType>()) {
conversionsOrFixes.push_back(
ConversionRestrictionKind::InoutToPointer);
}
break;
}
}
}
}
if (kind >= ConstraintKind::OperatorArgumentConversion) {
// If the RHS is an inout type, the LHS must be an @lvalue type.
if (auto *lvt = type1->getAs<LValueType>()) {
if (auto *iot = type2->getAs<InOutType>()) {
return matchTypes(lvt->getObjectType(), iot->getObjectType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::LValueConversion));
}
}
}
// A value of type T! can be converted to type U if T is convertible
// to U by force-unwrapping the source value.
// A value of type T, T?, or T! can be converted to type U? or U! if
// T is convertible to U.
if (!type1->is<LValueType>() && kind >= ConstraintKind::Subtype) {
enumerateOptionalConversionRestrictions(
type1, type2, kind, locator,
[&](ConversionRestrictionKind restriction) {
conversionsOrFixes.push_back(restriction);
});
}
// Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
// literals.
if (auto elt = locator.last()) {
if (elt->getKind() == ConstraintLocator::ClosureResult) {
if (kind >= ConstraintKind::Subtype &&
(type1->isUninhabited() || type2->isVoid())) {
increaseScore(SK_FunctionConversion);
return getTypeMatchSuccess();
}
}
}
if (kind == ConstraintKind::BindParam) {
if (auto *iot = dyn_cast<InOutType>(desugar1)) {
if (auto *lvt = dyn_cast<LValueType>(desugar2)) {
return matchTypes(iot->getObjectType(), lvt->getObjectType(),
ConstraintKind::Bind, subflags,
locator.withPathElement(
ConstraintLocator::LValueConversion));
}
}
}
// Attempt fixes iff it's allowed, both types are concrete and
// we are not in the middle of attempting one already.
bool attemptFixes =
shouldAttemptFixes() && !flags.contains(TMF_ApplyingFix);
// When we hit this point, we're committed to the set of potential
// conversions recorded thus far.
//
// If we should attempt fixes, add those to the list. They'll only be visited
// if there are no other possible solutions.
if (attemptFixes && kind >= ConstraintKind::Conversion) {
Type objectType1 = type1->getRValueType();
// If we have an optional type, try to force-unwrap it.
// FIXME: Should we also try '?'?
if (objectType1->getOptionalObjectType()) {
bool forceUnwrapPossible = true;
if (auto declRefExpr =
dyn_cast_or_null<DeclRefExpr>(locator.trySimplifyToExpr())) {
if (declRefExpr->getDecl()->isImplicit()) {
// The expression that provides the first type is implicit and never
// spelled out in source code, e.g. $match in an expression pattern.
// Thus we cannot force unwrap the first type
forceUnwrapPossible = false;
}
}
if (auto optTryExpr =
dyn_cast_or_null<OptionalTryExpr>(locator.trySimplifyToExpr())) {
auto subExprType = getType(optTryExpr->getSubExpr());
bool isSwift5OrGreater = TC.getLangOpts().isSwiftVersionAtLeast(5);
if (isSwift5OrGreater && (bool)subExprType->getOptionalObjectType()) {
// For 'try?' expressions, a ForceOptional fix converts 'try?'
// to 'try!'. If the sub-expression is optional, then a force-unwrap
// won't change anything in Swift 5+ because 'try?' already avoids
// adding an additional layer of Optional there.
forceUnwrapPossible = false;
}
}
if (forceUnwrapPossible) {
conversionsOrFixes.push_back(
ForceOptional::create(*this, getConstraintLocator(locator)));
}
}
// 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(
ForceDowncast::create(*this, type2, getConstraintLocator(locator)));
}
// If we could perform a bridging cast, try it.
if (auto bridged =
TC.getDynamicBridgedThroughObjCClass(DC, objectType1, type2)) {
// Note: don't perform this recovery for NSNumber;
bool useFix = true;
if (auto classType = bridged->getAs<ClassType>()) {
SmallString<16> scratch;
if (classType->getDecl()->isObjC() &&
classType->getDecl()->getObjCRuntimeName(scratch) == "NSNumber")
useFix = false;
}
if (useFix)
conversionsOrFixes.push_back(
ForceDowncast::create(*this, type2, getConstraintLocator(locator)));
}
if (type2->is<InOutType>()) {
if (type1->is<LValueType>()) {
// If we're converting an lvalue to an inout type, add the missing '&'.
conversionsOrFixes.push_back(
AddAddressOf::create(*this, getConstraintLocator(locator)));
} else {
// If we have a concrete type that's an rvalue, "fix" it.
conversionsOrFixes.push_back(
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
}
}
}
if (attemptFixes && type2->is<LValueType>()) {
conversionsOrFixes.push_back(
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
} else if (attemptFixes && kind == ConstraintKind::Bind && type1->is<LValueType>()) {
conversionsOrFixes.push_back(
TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
}
// Attempt to repair any failures identifiable at this point.
if (attemptFixes)
repairFailures(*this, type1, type2, conversionsOrFixes, locator);
if (conversionsOrFixes.empty())
return getTypeMatchFailure(locator);
// 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 = kind;
if (auto restriction = potential.getRestriction()) {
// Determine the constraint kind. For a deep equality constraint, only
// perform equality.
if (*restriction == ConversionRestrictionKind::DeepEquality)
constraintKind = ConstraintKind::Bind;
constraints.push_back(
Constraint::createRestricted(*this, constraintKind, *restriction,
type1, type2, fixedLocator));
if (constraints.back()->getKind() == ConstraintKind::Bind)
constraints.back()->setFavored();
continue;
}
auto fix = *potential.getFix();
constraints.push_back(
Constraint::createFixed(*this, constraintKind, fix, type1, type2,
fixedLocator));
}
// Sort favored constraints first.
std::sort(constraints.begin(), constraints.end(),
[&](Constraint *lhs, Constraint *rhs) -> bool {
if (lhs->isFavored() == rhs->isFavored())
return false;
return lhs->isFavored();
});
addDisjunctionConstraint(constraints, fixedLocator);
return getTypeMatchSuccess();
}
// For a single potential conversion, directly recurse, so that we
// don't allocate a new constraint or constraint locator.
auto formTypeMatchResult = [&](SolutionKind kind) {
switch (kind) {
case SolutionKind::Error:
return getTypeMatchFailure(locator);
case SolutionKind::Solved:
return getTypeMatchSuccess();
case SolutionKind::Unsolved:
return getTypeMatchAmbiguous();
}
llvm_unreachable("unhandled kind");
};
// Handle restrictions.
if (auto restriction = conversionsOrFixes[0].getRestriction()) {
return formTypeMatchResult(simplifyRestrictedConstraint(*restriction, type1,
type2, kind,
subflags, locator));
}
// Handle fixes.
auto fix = *conversionsOrFixes[0].getFix();
return formTypeMatchResult(simplifyFixConstraint(fix, type1, type2, kind,
subflags, locator));
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyConstructionConstraint(
Type valueType, FunctionType *fnType, TypeMatchOptions flags,
DeclContext *useDC,
FunctionRefKind functionRefKind, ConstraintLocator *locator) {
// Desugar the value type.
auto desugarValueType = valueType->getDesugaredType();
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::Unresolved:
case TypeKind::Error:
return SolutionKind::Error;
case TypeKind::GenericFunction:
case TypeKind::GenericTypeParam:
llvm_unreachable("unmapped dependent type");
case TypeKind::TypeVariable:
case TypeKind::DependentMember:
return SolutionKind::Unsolved;
case TypeKind::Tuple: {
// Tuple construction is simply tuple conversion.
Type argType = AnyFunctionType::composeInput(getASTContext(),
fnType->getParams(),
/*canonicalVararg=*/false);
Type resultType = fnType->getResult();
if (matchTypes(resultType, desugarValueType,
ConstraintKind::Bind,
flags,
ConstraintLocatorBuilder(locator)
.withPathElement(ConstraintLocator::ApplyFunction))
.isFailure())
return SolutionKind::Error;
return matchTypes(argType, valueType, ConstraintKind::Conversion,
getDefaultDecompositionOptions(flags), locator);
}
case TypeKind::Enum:
case TypeKind::Struct:
case TypeKind::Class:
case TypeKind::BoundGenericClass:
case TypeKind::BoundGenericEnum:
case TypeKind::BoundGenericStruct:
case TypeKind::PrimaryArchetype:
case TypeKind::OpenedArchetype:
case TypeKind::NestedArchetype:
case TypeKind::DynamicSelf:
case TypeKind::ProtocolComposition:
case TypeKind::Protocol:
// Break out to handle the actual construction below.
break;
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:
return SolutionKind::Error;
}
auto fnLocator = getConstraintLocator(locator,
ConstraintLocator::ApplyFunction);
auto memberType = createTypeVariable(fnLocator);
// The constructor will have function type T -> T2, for a fresh type
// variable T. T2 is the result type provided via the construction
// constraint itself.
addValueMemberConstraint(MetatypeType::get(valueType, TC.Context),
DeclBaseName::createConstructor(),
memberType,
useDC, functionRefKind,
/*outerAlternatives=*/{},
getConstraintLocator(
fnLocator,
ConstraintLocator::ConstructorMember));
// FIXME: Once TVO_PrefersSubtypeBinding is replaced with something
// better, we won't need the second type variable at all.
{
auto argType = createTypeVariable(
getConstraintLocator(locator, ConstraintLocator::ApplyArgument),
(TVO_CanBindToLValue |
TVO_CanBindToInOut |
TVO_PrefersSubtypeBinding));
addConstraint(ConstraintKind::FunctionInput, memberType, argType, locator);
}
addConstraint(ConstraintKind::ApplicableFunction, fnType, memberType,
fnLocator);
return SolutionKind::Solved;
}
ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
Type type,
Type protocol,
ConstraintKind kind,
ConstraintLocatorBuilder locator,
TypeMatchOptions flags) {
if (auto proto = protocol->getAs<ProtocolType>()) {
return simplifyConformsToConstraint(type, proto->getDecl(), kind,
locator, flags);
}
// Dig out the fixed type to which this type refers.
type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true);
return matchExistentialTypes(type, protocol, kind, flags, locator);
}
ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
Type type,
ProtocolDecl *protocol,
ConstraintKind kind,
ConstraintLocatorBuilder locator,
TypeMatchOptions flags) {
// Dig out the fixed type to which this type refers.
type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true);
// If we hit a type variable without a fixed type, we can't
// solve this yet.
if (type->isTypeVariableOrMember()) {
// If we're supposed to generate constraints, do so.
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::create(*this, kind, type, protocol->getDeclaredType(),
getConstraintLocator(locator)));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
}
/// Record the given conformance as the result, adding any conditional
/// requirements if necessary.
auto recordConformance = [&](ProtocolConformanceRef conformance) {
// Record the conformance.
CheckedConformances.push_back({getConstraintLocator(locator), conformance});
// This conformance may be conditional, in which case we need to consider
// those requirements as constraints too.
if (conformance.isConcrete()) {
unsigned index = 0;
for (const auto &req : conformance.getConditionalRequirements()) {
addConstraint(
req,
locator.withPathElement(
LocatorPathElt::getConditionalRequirementComponent(index++)));
}
}
return SolutionKind::Solved;
};
// For purposes of argument type matching, existential types don't need to
// conform -- they only need to contain the protocol, so check that
// separately.
switch (kind) {
case ConstraintKind::SelfObjectOfProtocol:
if (auto conformance =
TC.containsProtocol(type, protocol, DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::SkipConditionalRequirements))) {
return recordConformance(*conformance);
}
break;
case ConstraintKind::ConformsTo:
case ConstraintKind::LiteralConformsTo: {
// Check whether this type conforms to the protocol.
if (auto conformance =
TC.conformsToProtocol(
type, protocol, DC,
(ConformanceCheckFlags::InExpression|
ConformanceCheckFlags::SkipConditionalRequirements))) {
return recordConformance(*conformance);
}
break;
}
default:
llvm_unreachable("bad constraint kind");
}
if (!shouldAttemptFixes())
return SolutionKind::Error;
// See if there's anything we can do to fix the conformance:
if (auto optionalObjectType = type->getOptionalObjectType()) {
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
// The underlying type of an optional may conform to the protocol if the
// optional doesn't; suggest forcing if that's the case.
auto result = simplifyConformsToConstraint(
optionalObjectType, protocol, kind,
locator.withPathElement(LocatorPathElt::getGenericArgument(0)),
subflags);
if (result == SolutionKind::Solved) {
auto *fix = ForceOptional::create(*this, getConstraintLocator(locator));
if (recordFix(fix)) {
return SolutionKind::Error;
}
}
return result;
}
// Let's not try to fix missing conformance for Void
// and Never because that doesn't really make sense.
if (type->isVoid() || type->isUninhabited())
return SolutionKind::Error;
// If this is a generic requirement let's try to record that
// conformance is missing and consider this a success, which
// makes it much easier to diagnose problems like that.
{
SmallVector<LocatorPathElt, 4> path;
auto *anchor = locator.getLocatorParts(path);
if (!path.empty() && path.back().getKind() ==
ConstraintLocator::PathElementKind::TypeParameterRequirement) {
auto typeRequirement = path.back();
// Let's strip all of the unnecessary information from locator,
// diagnostics only care about anchor - to lookup type,
// and what was the requirement# which is not satisfied.
ConstraintLocatorBuilder requirement(getConstraintLocator(anchor));
auto *reqLoc =
getConstraintLocator(requirement.withPathElement(typeRequirement));
auto *fix = MissingConformance::create(*this, type, protocol, reqLoc);
if (!recordFix(fix))
return SolutionKind::Solved;
}
}
// There's nothing more we can do; fail.
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) {
// Array downcasts are handled specially.
if (cs->isArrayType(fromType) && cs->isArrayType(toType)) {
return CheckedCastKind::ArrayDowncast;
}
// Dictionary downcasts are handled specially.
if (cs->isDictionaryType(fromType) && cs->isDictionaryType(toType)) {
return CheckedCastKind::DictionaryDowncast;
}
// Set downcasts are handled specially.
if (cs->isSetType(fromType) && cs->isSetType(toType)) {
return CheckedCastKind::SetDowncast;
}
return CheckedCastKind::ValueCast;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyCheckedCastConstraint(
Type fromType, Type toType,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
/// Form an unresolved result.
auto formUnsolved = [&] {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::create(*this, ConstraintKind::CheckedCast, fromType,
toType, getConstraintLocator(locator)));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
};
do {
// Dig out the fixed type this type refers to.
fromType = getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true);
// If we hit a type variable without a fixed type, we can't
// solve this yet.
if (fromType->isTypeVariableOrMember())
return formUnsolved();
// Dig out the fixed type this type refers to.
toType = getFixedTypeRecursive(toType, flags, /*wantRValue=*/true);
// If we hit a type variable without a fixed type, we can't
// solve this yet.
if (toType->isTypeVariableOrMember())
return formUnsolved();
Type origFromType = fromType;
Type origToType = toType;
// Peel off optionals metatypes from the types, because we might cast through
// them.
toType = toType->lookThroughAllOptionalTypes();
fromType = fromType->lookThroughAllOptionalTypes();
// 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();
}
}
// We've decomposed the types further, so adopt the subflags.
flags = subflags;
// If nothing changed, we're done.
if (fromType.getPointer() == origFromType.getPointer() &&
toType.getPointer() == origToType.getPointer())
break;
} while (true);
auto kind = getCheckedCastKind(this, fromType, toType);
switch (kind) {
case CheckedCastKind::ArrayDowncast: {
auto fromBaseType = *isArrayType(fromType);
auto toBaseType = *isArrayType(toType);
return simplifyCheckedCastConstraint(fromBaseType, toBaseType, subflags,
locator);
}
case CheckedCastKind::DictionaryDowncast: {
Type fromKeyType, fromValueType;
std::tie(fromKeyType, fromValueType) = *isDictionaryType(fromType);
Type toKeyType, toValueType;
std::tie(toKeyType, toValueType) = *isDictionaryType(toType);
if (simplifyCheckedCastConstraint(fromKeyType, toKeyType, subflags,
locator) == SolutionKind::Error)
return SolutionKind::Error;
return simplifyCheckedCastConstraint(fromValueType, toValueType, subflags,
locator);
}
case CheckedCastKind::SetDowncast: {
auto fromBaseType = *isSetType(fromType);
auto toBaseType = *isSetType(toType);
return simplifyCheckedCastConstraint(fromBaseType, toBaseType, subflags,
locator);
}
case CheckedCastKind::ValueCast: {
// If casting among classes, and there are open
// type variables remaining, introduce a subtype constraint to help resolve
// them.
if (fromType->getClassOrBoundGenericClass()
&& toType->getClassOrBoundGenericClass()
&& (fromType->hasTypeVariable() || toType->hasTypeVariable())) {
addConstraint(ConstraintKind::Subtype, toType, fromType,
getConstraintLocator(locator));
}
return SolutionKind::Solved;
}
case CheckedCastKind::Coercion:
case CheckedCastKind::BridgingCoercion:
case CheckedCastKind::Unresolved:
llvm_unreachable("Not a valid result");
}
llvm_unreachable("Unhandled CheckedCastKind in switch.");
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyOptionalObjectConstraint(
Type first, Type second,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
// Resolve the optional type.
Type optLValueTy = getFixedTypeRecursive(first, flags, /*wantRValue=*/false);
Type optTy = optLValueTy->getRValueType();
if (optTy.getPointer() != optLValueTy.getPointer())
optTy = getFixedTypeRecursive(optTy, /*wantRValue=*/false);
if (optTy->isTypeVariableOrMember()) {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::create(*this, ConstraintKind::OptionalObject, optLValueTy,
second, getConstraintLocator(locator)));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
}
Type objectTy = optTy->getOptionalObjectType();
// If the base type is not optional, let's attempt a fix (if possible)
// and assume that `!` is just not there.
if (!objectTy) {
// Let's see if we can apply a specific fix here.
if (shouldAttemptFixes()) {
auto *fix =
RemoveUnwrap::create(*this, optTy, getConstraintLocator(locator));
if (recordFix(fix))
return SolutionKind::Error;
// If the fix was successful let's record
// "fixed" object type and continue.
objectTy = optTy;
} else {
// If fixes are not allowed, no choice but to fail.
return SolutionKind::Error;
}
}
// The object type is an lvalue if the optional was.
if (optLValueTy->is<LValueType>())
objectTy = LValueType::get(objectTy);
// Equate it to the other type in the constraint.
addConstraint(ConstraintKind::Bind, objectTy, second, locator);
return SolutionKind::Solved;
}
/// Attempt to simplify a function input or result constraint.
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyFunctionComponentConstraint(
ConstraintKind kind,
Type first, Type second,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
auto simplified = simplifyType(first);
unsigned unwrapCount = 0;
if (shouldAttemptFixes()) {
while (auto objectTy = simplified->getOptionalObjectType()) {
simplified = objectTy;
// Track how many times we do this so that we can record a fix for each.
++unwrapCount;
}
}
if (simplified->isTypeVariableOrMember()) {
if (!flags.contains(TMF_GenerateConstraints))
return SolutionKind::Unsolved;
addUnsolvedConstraint(
Constraint::create(*this, kind, simplified, second,
getConstraintLocator(locator)));
} else if (auto *funcTy = simplified->getAs<FunctionType>()) {
// Equate it to the other type in the constraint.
Type type;
ConstraintLocator::PathElementKind locKind;
if (kind == ConstraintKind::FunctionInput) {
type = AnyFunctionType::composeInput(getASTContext(),
funcTy->getParams(),
/*canonicalVararg=*/false);
locKind = ConstraintLocator::FunctionArgument;
} else if (kind == ConstraintKind::FunctionResult) {
type = funcTy->getResult();
locKind = ConstraintLocator::FunctionResult;
} else {
llvm_unreachable("Bad function component constraint kind");
}
addConstraint(ConstraintKind::Bind, type, second,
locator.withPathElement(locKind));
} else {
return SolutionKind::Error;
}
if (unwrapCount > 0) {
auto *fix = ForceOptional::create(*this, getConstraintLocator(locator));
while (unwrapCount-- > 0) {
if (recordFix(fix))
return SolutionKind::Error;
}
}
return SolutionKind::Solved;
}
/// Retrieve the argument labels that are provided for a member
/// reference at the given locator.
static Optional<ConstraintSystem::ArgumentLabelState>
getArgumentLabels(ConstraintSystem &cs, ConstraintLocatorBuilder locator) {
SmallVector<LocatorPathElt, 2> parts;
Expr *anchor = locator.getLocatorParts(parts);
if (!anchor)
return None;
while (!parts.empty()) {
if (parts.back().getKind() == ConstraintLocator::Member ||
parts.back().getKind() == ConstraintLocator::SubscriptMember) {
parts.pop_back();
continue;
}
if (parts.back().getKind() == ConstraintLocator::ApplyFunction) {
if (auto applyExpr = dyn_cast<ApplyExpr>(anchor)) {
anchor = applyExpr->getSemanticFn();
}
parts.pop_back();
continue;
}
if (parts.back().getKind() == ConstraintLocator::ConstructorMember) {
// FIXME: Workaround for strange anchor on ConstructorMember locators.
if (auto optionalWrapper = dyn_cast<BindOptionalExpr>(anchor))
anchor = optionalWrapper->getSubExpr();
else if (auto forceWrapper = dyn_cast<ForceValueExpr>(anchor))
anchor = forceWrapper->getSubExpr();
parts.pop_back();
continue;
}
break;
}
if (!parts.empty())
return None;
auto known = cs.ArgumentLabels.find(cs.getConstraintLocator(anchor));
if (known == cs.ArgumentLabels.end())
return None;
return known->second;
}
/// Return true if the specified type or a super-class/super-protocol has the
/// @dynamicMemberLookup attribute on it. This implementation is not
/// particularly fast in the face of deep class hierarchies or lots of protocol
/// conformances, but this is fine because it doesn't get invoked in the normal
/// name lookup path (only when lookup is about to fail).
static bool hasDynamicMemberLookupAttribute(Type type,
llvm::DenseMap<CanType, bool> &DynamicMemberLookupCache) {
auto canType = type->getCanonicalType();
auto it = DynamicMemberLookupCache.find(canType);
if (it != DynamicMemberLookupCache.end()) return it->second;
// Calculate @dynamicMemberLookup attribute for composite types with multiple
// components (protocol composition types and archetypes).
auto calculateForComponentTypes =
[&](ArrayRef<Type> componentTypes) -> bool {
for (auto componentType : componentTypes)
if (hasDynamicMemberLookupAttribute(componentType,
DynamicMemberLookupCache))
return true;
return false;
};
auto calculate = [&]() -> bool {
// If this is an archetype type, check if any types it conforms to
// (superclass or protocols) have the attribute.
if (auto archetype = dyn_cast<ArchetypeType>(canType)) {
SmallVector<Type, 2> componentTypes;
for (auto protocolDecl : archetype->getConformsTo())
componentTypes.push_back(protocolDecl->getDeclaredType());
if (auto superclass = archetype->getSuperclass())
componentTypes.push_back(superclass);
return calculateForComponentTypes(componentTypes);
}
// If this is a protocol composition, check if any of its members have the
// attribute.
if (auto protocolComp = dyn_cast<ProtocolCompositionType>(canType))
return calculateForComponentTypes(protocolComp->getMembers());
// Otherwise, this must be a nominal type.
// Dynamic member lookup doesn't work for tuples, etc.
auto nominal = canType->getAnyNominal();
if (!nominal) return false;
// If this type conforms to a protocol with the attribute, then return true.
for (auto p : nominal->getAllProtocols())
if (p->getAttrs().hasAttribute<DynamicMemberLookupAttr>())
return true;
// Walk superclasses, if present.
llvm::SmallPtrSet<const NominalTypeDecl*, 8> visitedDecls;
while (1) {
// If we found a circular parent class chain, reject this.
if (!visitedDecls.insert(nominal).second)
return false;
// If this type has the attribute on it, then yes!
if (nominal->getAttrs().hasAttribute<DynamicMemberLookupAttr>())
return true;
// If this is a class with a super class, check super classes as well.
if (auto *cd = dyn_cast<ClassDecl>(nominal)) {
if (auto superClass = cd->getSuperclassDecl()) {
nominal = superClass;
continue;
}
}
return false;
}
};
auto result = calculate();
// Cache the result if the type does not contain type variables.
if (!type->hasTypeVariable())
DynamicMemberLookupCache[canType] = result;
return result;
}
/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
/// perform a lookup into the specified base type to find a candidate list.
/// The list returned includes the viable candidates as well as the unviable
/// ones (along with reasons why they aren't viable).
///
/// If includeInaccessibleMembers is set to true, this burns compile time to
/// try to identify and classify inaccessible members that may be being
/// referenced.
MemberLookupResult ConstraintSystem::
performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
Type baseTy, FunctionRefKind functionRefKind,
ConstraintLocator *memberLocator,
bool includeInaccessibleMembers) {
Type baseObjTy = baseTy->getRValueType();
Type instanceTy = baseObjTy;
if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
instanceTy = baseObjMeta->getInstanceType();
}
if (instanceTy->isTypeVariableOrMember() ||
instanceTy->is<UnresolvedType>()) {
MemberLookupResult result;
result.OverallResult = MemberLookupResult::Unsolved;
return result;
}
// Okay, start building up the result list.
MemberLookupResult result;
result.OverallResult = MemberLookupResult::HasResults;
// If we're looking for a subscript, consider key path operations.
if (memberName.isSimpleName() &&
memberName.getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
result.ViableCandidates.push_back(
OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication));
}
// If the base type is a tuple type, look for the named or indexed member
// of the tuple.
if (auto baseTuple = baseObjTy->getAs<TupleType>()) {
// Tuples don't have compound-name members.
if (!memberName.isSimpleName() || memberName.isSpecial())
return result; // No result.
StringRef nameStr = memberName.getBaseIdentifier().str();
int fieldIdx = -1;
// Resolve a number reference into the tuple type.
unsigned Value = 0;
if (!nameStr.getAsInteger(10, Value) &&
Value < baseTuple->getNumElements()) {
fieldIdx = Value;
} else {
fieldIdx = baseTuple->getNamedElementId(memberName.getBaseIdentifier());
}
if (fieldIdx == -1)
return result; // No result.
// Add an overload set that selects this field.
result.ViableCandidates.push_back(OverloadChoice(baseTy, fieldIdx));
return result;
}
if (auto *selfTy = instanceTy->getAs<DynamicSelfType>())
instanceTy = selfTy->getSelfType();
if (!instanceTy->mayHaveMembers())
return result;
// If we have a simple name, determine whether there are argument
// labels we can use to restrict the set of lookup results.
Optional<ArgumentLabelState> argumentLabels;
if (memberName.isSimpleName()) {
argumentLabels = getArgumentLabels(*this,
ConstraintLocatorBuilder(memberLocator));
// If we're referencing AnyObject and we have argument labels, put
// the argument labels into the name: we don't want to look for
// anything else, because the cost of the general search is so
// high.
if (baseObjTy->isAnyObject() && argumentLabels) {
memberName = DeclName(TC.Context, memberName.getBaseName(),
argumentLabels->Labels);
argumentLabels.reset();
}
}
/// Determine whether the given declaration has compatible argument
/// labels.
auto hasCompatibleArgumentLabels = [&argumentLabels](Type baseObjTy,
ValueDecl *decl) -> bool {
if (!argumentLabels)
return true;
// This is a member lookup, which generally means that the call arguments
// (if we have any) will apply to the second level of parameters, with
// the member lookup binding the first level. But there are cases where
// we can get an unapplied declaration reference back.
bool hasCurriedSelf;
if (baseObjTy->is<ModuleType>()) {
hasCurriedSelf = false;
} else if (baseObjTy->is<AnyMetatypeType>() && decl->isInstanceMember()) {
hasCurriedSelf = false;
} else {
hasCurriedSelf = true;
}
return areConservativelyCompatibleArgumentLabels(decl, hasCurriedSelf,
argumentLabels->Labels,
argumentLabels->HasTrailingClosure);
};
// Look for members within the base.
LookupResult &lookup = lookupMember(instanceTy, memberName);
// If this is true, we're using type construction syntax (Foo()) rather
// than an explicit call to `init` (Foo.init()).
bool isImplicitInit = false;
TypeBase *favoredType = nullptr;
if (memberName.isSimpleName(DeclBaseName::createConstructor())) {
SmallVector<LocatorPathElt, 2> parts;
if (auto *anchor = memberLocator->getAnchor()) {
auto path = memberLocator->getPath();
if (!path.empty())
if (path.back().getKind() == ConstraintLocator::ConstructorMember)
isImplicitInit = true;
if (auto applyExpr = dyn_cast<ApplyExpr>(anchor)) {
auto argExpr = applyExpr->getArg();
favoredType = getFavoredType(argExpr);
if (!favoredType) {
optimizeConstraints(argExpr);
favoredType = getFavoredType(argExpr);
}
}
}
}
// If the instance type is String bridged to NSString, compute
// the type we'll look in for bridging.
Type bridgedType;
if (baseObjTy->getAnyNominal() == TC.Context.getStringDecl()) {
if (Type classType = TC.Context.getBridgedToObjC(DC, instanceTy)) {
bridgedType = classType;
}
}
bool labelMismatch = false;
// Local function that adds the given declaration if it is a
// reasonable choice.
auto addChoice = [&](OverloadChoice candidate) {
auto decl = candidate.getDecl();
// If the result is invalid, skip it.
TC.validateDecl(decl);
if (decl->isInvalid()) {
result.markErrorAlreadyDiagnosed();
return;
}
// FIXME: Deal with broken recursion
if (!decl->hasInterfaceType())
return;
// Dig out the instance type and figure out what members of the instance type
// we are going to see.
auto baseTy = candidate.getBaseType();
auto baseObjTy = baseTy->getRValueType();
bool hasInstanceMembers = false;
bool hasInstanceMethods = false;
bool hasStaticMembers = false;
Type instanceTy = baseObjTy;
if (baseObjTy->is<ModuleType>()) {
hasStaticMembers = true;
} else if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
instanceTy = baseObjMeta->getInstanceType();
if (baseObjMeta->is<ExistentialMetatypeType>()) {
// An instance of an existential metatype is a concrete type conforming
// to the existential, say Self. Instance members of the concrete type
// have type Self -> T -> U, but we don't know what Self is at compile
// time so we cannot refer to them. Static methods are fine, on the other
// hand -- we already know that they do not have Self or associated type
// requirements, since otherwise we would not be able to refer to the
// existential metatype in the first place.
hasStaticMembers = true;
} else if (instanceTy->isExistentialType()) {
// A protocol metatype has instance methods with type P -> T -> U, but
// not instance properties or static members -- the metatype value itself
// doesn't give us a witness so there's no static method to bind.
hasInstanceMethods = true;
} else {
// Metatypes of nominal types and archetypes have instance methods and
// static members, but not instance properties.
// FIXME: partial application of properties
hasInstanceMethods = true;
hasStaticMembers = true;
}
// If we're at the root of an unevaluated context, we can
// reference instance members on the metatype.
if (memberLocator &&
UnevaluatedRootExprs.count(memberLocator->getAnchor())) {
hasInstanceMembers = true;
}
} else {
// Otherwise, we can access all instance members.
hasInstanceMembers = true;
hasInstanceMethods = true;
}
// If the argument labels for this result are incompatible with
// the call site, skip it.
if (!hasCompatibleArgumentLabels(baseObjTy, decl)) {
labelMismatch = true;
result.addUnviable(candidate, MemberLookupResult::UR_LabelMismatch);
return;
}
// If our base is an existential type, we can't make use of any
// member whose signature involves associated types.
if (instanceTy->isExistentialType()) {
if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) {
if (!proto->isAvailableInExistential(decl)) {
result.addUnviable(candidate,
MemberLookupResult::UR_UnavailableInExistential);
return;
}
}
}
// If the invocation's argument expression has a favored type,
// use that information to determine whether a specific overload for
// the candidate should be favored.
if (isa<ConstructorDecl>(decl) && favoredType &&
result.FavoredChoice == ~0U) {
auto *ctor = cast<ConstructorDecl>(decl);
// Only try and favor monomorphic initializers.
if (!ctor->isGenericContext()) {
auto args = ctor->getMethodInterfaceType()
->castTo<FunctionType>()->getParams();
auto argType = AnyFunctionType::composeInput(getASTContext(), args,
/*canonicalVarargs=*/false);
if (argType->isEqual(favoredType))
if (!decl->getAttrs().isUnavailable(getASTContext()))
result.FavoredChoice = result.ViableCandidates.size();
}
}
// See if we have an instance method, instance member or static method,
// and check if it can be accessed on our base type.
if (decl->isInstanceMember()) {
if ((isa<FuncDecl>(decl) && !hasInstanceMethods) ||
(!isa<FuncDecl>(decl) && !hasInstanceMembers)) {
result.addUnviable(candidate,
MemberLookupResult::UR_InstanceMemberOnType);
return;
}
// If the underlying type of a typealias is fully concrete, it is legal
// to access the type with a protocol metatype base.
} else if (instanceTy->isExistentialType() &&
isa<TypeAliasDecl>(decl) &&
!cast<TypeAliasDecl>(decl)->getInterfaceType()->hasTypeParameter()) {
/* We're OK */
} else {
if (!hasStaticMembers) {
result.addUnviable(candidate,
MemberLookupResult::UR_TypeMemberOnInstance);
return;
}
}
// If we have an rvalue base, make sure that the result isn't 'mutating'
// (only valid on lvalues).
if (!baseTy->is<AnyMetatypeType>() &&
!baseTy->is<LValueType>() &&
decl->isInstanceMember()) {
if (auto *FD = dyn_cast<FuncDecl>(decl))
if (FD->isMutating()) {
result.addUnviable(candidate,
MemberLookupResult::UR_MutatingMemberOnRValue);
return;
}
// Subscripts and computed properties are ok on rvalues so long
// as the getter is nonmutating.
if (auto storage = dyn_cast<AbstractStorageDecl>(decl)) {
if (storage->isGetterMutating()) {
result.addUnviable(candidate,
MemberLookupResult::UR_MutatingGetterOnRValue);
return;
}
}
}
// Otherwise, we're good, add the candidate to the list.
result.addViable(candidate);
};
// Local function that turns a ValueDecl into a properly configured
// OverloadChoice.
auto getOverloadChoice = [&](ValueDecl *cand, bool isBridged,
bool isUnwrappedOptional) -> OverloadChoice {
// If we're looking into an existential type, check whether this
// result was found via dynamic lookup.
if (instanceTy->isAnyObject()) {
assert(cand->getDeclContext()->isTypeContext() && "Dynamic lookup bug");
// We found this declaration via dynamic lookup, record it as such.
return OverloadChoice::getDeclViaDynamic(baseTy, cand, functionRefKind);
}
// If we have a bridged type, we found this declaration via bridging.
if (isBridged)
return OverloadChoice::getDeclViaBridge(bridgedType, cand,
functionRefKind);
// If we got the choice by unwrapping an optional type, unwrap the base
// type.
if (isUnwrappedOptional) {
auto ovlBaseTy = MetatypeType::get(baseTy->castTo<MetatypeType>()
->getInstanceType()
->getOptionalObjectType());
return OverloadChoice::getDeclViaUnwrappedOptional(ovlBaseTy, cand,
functionRefKind);
}
return OverloadChoice(baseTy, cand, functionRefKind);
};
// Add all results from this lookup.
retry_after_fail:
labelMismatch = false;
for (auto result : lookup)
addChoice(getOverloadChoice(result.getValueDecl(),
/*isBridged=*/false,
/*isUnwrappedOptional=*/false));
// Backward compatibility hack. In Swift 4, `init` and init were
// the same name, so you could write "foo.init" to look up a
// method or property named `init`.
if (!TC.Context.isSwiftVersionAtLeast(5) &&
memberName.getBaseName() == DeclBaseName::createConstructor() &&
!isImplicitInit) {
auto &compatLookup = lookupMember(instanceTy,
TC.Context.getIdentifier("init"));
for (auto result : compatLookup)
addChoice(getOverloadChoice(result.getValueDecl(),
/*isBridged=*/false,
/*isUnwrappedOptional=*/false));
}
// If the instance type is a bridged to an Objective-C type, perform
// a lookup into that Objective-C type.
if (bridgedType) {
LookupResult &bridgedLookup = lookupMember(bridgedType, memberName);
ModuleDecl *foundationModule = nullptr;
for (auto result : bridgedLookup) {
// Ignore results from the Objective-C "Foundation"
// module. Those core APIs are explicitly provided by the
// Foundation module overlay.
auto module = result.getValueDecl()->getModuleContext();
if (foundationModule) {
if (module == foundationModule)
continue;
} else if (ClangModuleUnit::hasClangModule(module) &&
module->getName().str() == "Foundation") {
// Cache the foundation module name so we don't need to look
// for it again.
foundationModule = module;
continue;
}
addChoice(getOverloadChoice(result.getValueDecl(),
/*isBridged=*/true,
/*isUnwrappedOptional=*/false));
}
}
// If we're looking into a metatype for an unresolved member lookup, look
// through optional types.
//
// FIXME: The short-circuit here is lame.
if (result.ViableCandidates.empty() &&
baseObjTy->is<AnyMetatypeType>() &&
constraintKind == ConstraintKind::UnresolvedValueMember) {
if (auto objectType = instanceTy->getOptionalObjectType()) {
if (objectType->mayHaveMembers()) {
LookupResult &optionalLookup = lookupMember(objectType, memberName);
for (auto result : optionalLookup)
addChoice(getOverloadChoice(result.getValueDecl(),
/*bridged*/false,
/*isUnwrappedOptional=*/true));
}
}
}
// If we're about to fail lookup, but we are looking for members in a type
// with the @dynamicMemberLookup attribute, then we resolve a reference
// to a `subscript(dynamicMember:)` method and pass the member name as a
// string parameter.
if (result.ViableCandidates.empty() &&
constraintKind == ConstraintKind::ValueMember &&
memberName.isSimpleName() && !memberName.isSpecial()) {
auto name = memberName.getBaseIdentifier();
if (hasDynamicMemberLookupAttribute(instanceTy, DynamicMemberLookupCache)) {
auto &ctx = getASTContext();
// Recursively look up `subscript(dynamicMember:)` methods in this type.
auto subscriptName =
DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember);
auto subscripts = performMemberLookup(constraintKind,
subscriptName,
baseTy, functionRefKind,
memberLocator,
includeInaccessibleMembers);
// Reflect the candidates found as `DynamicMemberLookup` results.
for (auto candidate : subscripts.ViableCandidates) {
auto decl = cast<SubscriptDecl>(candidate.getDecl());
if (isValidDynamicMemberLookupSubscript(decl, DC, TC))
result.addViable(
OverloadChoice::getDynamicMemberLookup(baseTy, decl, name));
}
for (auto candidate : subscripts.UnviableCandidates) {
auto decl = candidate.first.getDecl();
auto choice = OverloadChoice::getDynamicMemberLookup(baseTy, decl,name);
result.addUnviable(choice, candidate.second);
}
}
}
// If we rejected some possibilities due to an argument-label
// mismatch and ended up with nothing, try again ignoring the
// labels. This allows us to perform typo correction on the labels.
if (result.ViableCandidates.empty() && labelMismatch && shouldAttemptFixes()){
argumentLabels.reset();
goto retry_after_fail;
}
// If we have no viable or unviable candidates, and we're generating,
// diagnostics, rerun the query with inaccessible members included, so we can
// include them in the unviable candidates list.
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
includeInaccessibleMembers) {
NameLookupOptions lookupOptions = defaultMemberLookupOptions;
// Ignore access control so we get candidates that might have been missed
// before.
lookupOptions |= NameLookupFlags::IgnoreAccessControl;
// This is only used for diagnostics, so always use KnownPrivate.
lookupOptions |= NameLookupFlags::KnownPrivate;
auto lookup = TC.lookupMember(DC, instanceTy,
memberName, lookupOptions);
for (auto entry : lookup) {
auto *cand = entry.getValueDecl();
// If the result is invalid, skip it.
TC.validateDecl(cand);
if (cand->isInvalid()) {
result.markErrorAlreadyDiagnosed();
return result;
}
// FIXME: Deal with broken recursion
if (!cand->hasInterfaceType())
continue;
result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false,
/*isUnwrappedOptional=*/false),
MemberLookupResult::UR_Inaccessible);
}
}
return result;
}
ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
ConstraintKind kind, Type baseTy, DeclName member, Type memberTy,
DeclContext *useDC, FunctionRefKind functionRefKind,
ArrayRef<OverloadChoice> outerAlternatives, TypeMatchOptions flags,
ConstraintLocatorBuilder locatorB) {
// We'd need to record original base type because it might be a type
// variable representing another missing member.
auto origBaseTy = baseTy;
// Resolve the base type, if we can. If we can't resolve the base type,
// then we can't solve this constraint.
baseTy = simplifyType(baseTy, flags);
Type baseObjTy = baseTy->getRValueType();
auto locator = getConstraintLocator(locatorB);
MemberLookupResult result =
performMemberLookup(kind, member, baseTy, functionRefKind, locator,
/*includeInaccessibleMembers*/false);
switch (result.OverallResult) {
case MemberLookupResult::Unsolved:
// If requested, generate a constraint.
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::createMemberOrOuterDisjunction(*this, kind, baseTy, memberTy, member, useDC,
functionRefKind, outerAlternatives, locator));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
case MemberLookupResult::ErrorAlreadyDiagnosed:
return SolutionKind::Error;
case MemberLookupResult::HasResults:
// Keep going!
break;
}
// If we found viable candidates, then we're done!
if (!result.ViableCandidates.empty()) {
addOverloadSet(memberTy, result.ViableCandidates, useDC, locator,
result.getFavoredChoice(), outerAlternatives);
return SolutionKind::Solved;
}
// If we found some unviable results, then fail, but without recovery.
if (!result.UnviableCandidates.empty())
return SolutionKind::Error;
// If the lookup found no hits at all (either viable or unviable), diagnose it
// as such and try to recover in various ways.
if (shouldAttemptFixes()) {
// Let's record missing member in constraint system, this helps to prevent
// stacking up fixes for the same member, because e.g. if its base was of
// optional type, we'd re-introduce member constraint with optional stripped
// off to see if the problem is related to base not being explicitly unwrapped.
if (!MissingMembers.insert(locator))
return SolutionKind::Error;
if (baseObjTy->getOptionalObjectType()) {
// If the base type was an optional, look through it.
// If the base type is optional because we haven't chosen to force an
// implicit optional, don't try to fix it. The IUO will be forced instead.
if (auto dotExpr = dyn_cast<UnresolvedDotExpr>(locator->getAnchor())) {
auto baseExpr = dotExpr->getBase();
auto resolvedOverload = getResolvedOverloadSets();
while (resolvedOverload) {
if (resolvedOverload->Locator->getAnchor() == baseExpr) {
if (resolvedOverload->Choice
.isImplicitlyUnwrappedValueOrReturnValue())
return SolutionKind::Error;
break;
}
resolvedOverload = resolvedOverload->Previous;
}
}
// The result of the member access can either be the expected member type
// (for '!' or optional members with '?'), or the original member type
// with one extra level of optionality ('?' with non-optional members).
auto innerTV = createTypeVariable(locator, TVO_CanBindToLValue);
Type optTy = getTypeChecker().getOptionalType(
locator->getAnchor()->getSourceRange().Start, innerTV);
SmallVector<Constraint *, 2> optionalities;
auto nonoptionalResult = Constraint::createFixed(
*this, ConstraintKind::Bind,
UnwrapOptionalBase::create(*this, member, locator), innerTV, memberTy,
locator);
auto optionalResult = Constraint::createFixed(
*this, ConstraintKind::Bind,
UnwrapOptionalBase::createWithOptionalResult(*this, member, locator),
optTy, memberTy, locator);
optionalities.push_back(nonoptionalResult);
optionalities.push_back(optionalResult);
addDisjunctionConstraint(optionalities, locator);
// Look through one level of optional.
addValueMemberConstraint(baseObjTy->getOptionalObjectType(), member,
innerTV, useDC, functionRefKind,
outerAlternatives, locator);
return SolutionKind::Solved;
}
auto solveWithNewBaseOrName = [&](Type baseType,
DeclName memberName) -> SolutionKind {
// Let's re-enable fixes for this member, because
// the base or member name has been changed.
MissingMembers.remove(locator);
return simplifyMemberConstraint(kind, baseType, memberName, memberTy,
useDC, functionRefKind, outerAlternatives,
flags, locatorB);
};
if (auto *funcType = baseTy->getAs<FunctionType>()) {
// We can't really suggest anything useful unless
// function takes no arguments, otherwise it
// would make sense to report this a missing member.
if (funcType->getNumParams() == 0) {
auto result = solveWithNewBaseOrName(funcType->getResult(), member);
// If there is indeed a member with given name in result type
// let's return, otherwise let's fall-through and report
// this problem as a missing member.
if (result == SolutionKind::Solved)
return recordFix(InsertExplicitCall::create(*this, locator))
? SolutionKind::Error
: SolutionKind::Solved;
}
}
// Instead of using subscript operator spelled out `subscript` directly.
if (member.getBaseName() == getTokenText(tok::kw_subscript)) {
auto result =
solveWithNewBaseOrName(baseTy, DeclBaseName::createSubscript());
// Looks like it was indeed meant to be a subscript operator.
if (result == SolutionKind::Solved)
return recordFix(UseSubscriptOperator::create(*this, locator))
? SolutionKind::Error
: SolutionKind::Solved;
}
// FIXME(diagnostics): This is more of a hack than anything.
// Let's not try to suggest that there is no member related to an
// obscure underscored type, the real problem would be somewhere
// else. This helps to diagnose pattern matching cases.
{
if (auto *metatype = baseTy->getAs<MetatypeType>()) {
auto instanceTy = metatype->getInstanceType();
if (auto *NTD = instanceTy->getAnyNominal()) {
if (NTD->getName() == getASTContext().Id_OptionalNilComparisonType)
return SolutionKind::Error;
}
}
}
// FIXME(diagnostics): Errors related to `AnyObject` could be diagnosed
// better in the future, relevant failure information has to be extracted
// from `performMemberLookup` result, in order to figure out if it was a
// simple labeling or # of arguments mismatch, or member with requested name
// really doesn't exist.
if (baseTy->isAnyObject())
return SolutionKind::Error;
result = performMemberLookup(kind, member, baseTy, functionRefKind, locator,
/*includeInaccessibleMembers*/ true);
// FIXME(diagnostics): If there were no viable results, but there are
// unviable ones, we'd have to introduce fix for each specific problem.
if (!result.UnviableCandidates.empty())
return SolutionKind::Error;
// Since member with given base and name doesn't exist, let's try to
// fake its presence based on use, that makes it possible to diagnose
// problems related to member lookup more precisely.
auto *fix =
DefineMemberBasedOnUse::create(*this, origBaseTy, member, locator);
if (recordFix(fix))
return SolutionKind::Error;
// Allow member type to default to `Any` to make it possible to form
// solutions when contextual type of the result cannot be deduced e.g.
// `let _ = x.foo`.
addConstraint(ConstraintKind::Defaultable, memberTy,
getASTContext().TheAnyType, locator);
return SolutionKind::Solved;
}
return SolutionKind::Error;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyDefaultableConstraint(
Type first, Type second,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
first = getFixedTypeRecursive(first, flags, true);
if (first->isTypeVariableOrMember()) {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::create(*this, ConstraintKind::Defaultable, first, second,
getConstraintLocator(locator)));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
}
// Otherwise, any type is fine.
return SolutionKind::Solved;
}
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyDynamicTypeOfConstraint(
Type type1, Type type2,
TypeMatchOptions flags,
ConstraintLocatorBuilder locator) {
TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
// Local function to form an unsolved result.
auto formUnsolved = [&] {
if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint(
Constraint::create(*this, ConstraintKind::DynamicTypeOf, type1, type2,
getConstraintLocator(locator)));
return SolutionKind::Solved;
}
return SolutionKind::Unsolved;
};
// Solve forward.
type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
if (!type2->isTypeVariableOrMember()) {
Type dynamicType2;
if (type2->isAnyExistentialType()) {
dynamicType2 = ExistentialMetatypeType::get(type2);
} else {
dynamicType2 = MetatypeType::get(type2);
}
return matchTypes(type1, dynamicType2, ConstraintKind::Bind, subflags,
locator);
}
// Okay, can't solve forward. See what we can do backwards.
type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true);
if (type1->isTypeVariableOrMember())
return formUnsolved();
// 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,
ConstraintKind::Bind,