//===--- 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 "CSDiagnostics.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckEffects.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/PackExpansionMatcher.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Requirement.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/StringExtras.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Sema/CSFix.h"
#include "swift/Sema/ConstraintSystem.h"
#include "swift/Sema/IDETypeChecking.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/Compiler.h"
using namespace swift;
using namespace constraints;
MatchCallArgumentListener::~MatchCallArgumentListener() { }
bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; }
std::optional
MatchCallArgumentListener::missingArgument(unsigned paramIdx,
unsigned argInsertIdx) {
return std::nullopt;
}
bool MatchCallArgumentListener::missingLabel(unsigned paramIdx) { return true; }
bool MatchCallArgumentListener::extraneousLabel(unsigned paramIdx) {
return true;
}
bool MatchCallArgumentListener::incorrectLabel(unsigned paramIdx) {
return true;
}
bool MatchCallArgumentListener::outOfOrderArgument(
unsigned argIdx, unsigned prevArgIdx, ArrayRef bindings) {
return true;
}
bool MatchCallArgumentListener::relabelArguments(ArrayRef newNames){
return true;
}
bool MatchCallArgumentListener::shouldClaimArgDuringRecovery(unsigned argIdx) {
return true;
}
bool MatchCallArgumentListener::canClaimArgIgnoringNameMismatch(
const AnyFunctionType::Param &arg) {
return false;
}
/// 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 std::optional 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 std::nullopt;
// 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 std::nullopt;
return dist;
}
bool constraints::isPackExpansionType(Type type) {
if (type->is())
return true;
if (auto *typeVar = type->getAs())
return typeVar->getImpl().isPackExpansion();
return false;
}
bool constraints::isSingleUnlabeledPackExpansionTuple(Type type) {
auto *tuple = type->getRValueType()->getAs();
return tuple && (tuple->getNumElements() == 1) &&
isPackExpansionType(tuple->getElementType(0)) &&
!tuple->getElement(0).hasName();
}
Type constraints::getPatternTypeOfSingleUnlabeledPackExpansionTuple(Type type) {
if (isSingleUnlabeledPackExpansionTuple(type)) {
auto tuple = type->getRValueType()->castTo();
const auto &tupleElement = tuple->getElementType(0);
if (auto *expansion = tupleElement->getAs()) {
return expansion->getPatternType();
}
if (auto *typeVar = tupleElement->getAs()) {
auto *locator = typeVar->getImpl().getLocator();
if (auto expansionElement =
locator->getLastElementAs()) {
return expansionElement->getOpenedType()->getPatternType();
}
}
}
return {};
}
bool constraints::containsPackExpansionType(ArrayRef params) {
return llvm::any_of(params, [&](const auto ¶m) {
return isPackExpansionType(param.getPlainType());
});
}
bool constraints::containsPackExpansionType(TupleType *tuple) {
return llvm::any_of(tuple->getElements(), [&](const auto &elt) {
return isPackExpansionType(elt.getType());
});
}
bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy,
const ValueDecl *decl) {
assert(decl->getDeclContext()->isTypeContext() &&
"Expected a member reference");
// For a reference to an instance method on a metatype, we want to keep the
// curried self.
if (decl->isInstanceMember()) {
assert(baseTy);
if (isa(decl) &&
baseTy->getRValueType()->is())
return false;
}
// Otherwise the reference applies self.
return true;
}
static bool areConservativelyCompatibleArgumentLabels(
ConstraintSystem &cs, OverloadChoice choice,
SmallVectorImpl &args,
MatchCallArgumentListener &listener,
std::optional unlabeledTrailingClosureArgIndex) {
ValueDecl *decl = nullptr;
switch (choice.getKind()) {
case OverloadChoiceKind::Decl:
case OverloadChoiceKind::DeclViaBridge:
case OverloadChoiceKind::DeclViaDynamic:
case OverloadChoiceKind::DeclViaUnwrappedOptional:
decl = choice.getDecl();
break;
// KeyPath application is not filtered in `performMemberLookup`.
case OverloadChoiceKind::KeyPathApplication:
case OverloadChoiceKind::DynamicMemberLookup:
case OverloadChoiceKind::KeyPathDynamicMemberLookup:
case OverloadChoiceKind::TupleIndex:
case OverloadChoiceKind::MaterializePack:
case OverloadChoiceKind::ExtractFunctionIsolation:
return true;
}
// If this is a member lookup, the call arguments (if we have any) will
// generally be applied to the second level of parameters, with the member
// lookup applying the curried self at the first level. But there are cases
// where we can get an unapplied declaration reference back.
auto hasAppliedSelf =
decl->hasCurriedSelf() &&
doesMemberRefApplyCurriedSelf(choice.getBaseType(), decl);
AnyFunctionType *fnType = nullptr;
if (decl->hasParameterList()) {
fnType = decl->getInterfaceType()->castTo();
if (hasAppliedSelf) {
fnType = fnType->getResult()->getAs();
assert(fnType && "Parameter list curry level does not match type");
}
} else if (auto *VD = dyn_cas