Merge pull request #59432 from xedin/rdar-94959816

[ConstraintSystem] Allow injecting `callAsFunction` after defaulted arguments
This commit is contained in:
Pavel Yaskevich
2022-06-27 13:31:45 -07:00
committed by GitHub
2 changed files with 131 additions and 8 deletions

View File

@@ -467,6 +467,35 @@ static bool matchCallArgumentsImpl(
return;
}
// Let's consider current closure to be extraneous if:
//
// - current parameter has a default value and doesn't accept a trailing
// closure; and
// - no other free parameter after this one accepts a trailing closure via
// forward or backward scan. This check makes sure that it's safe to
// reject and push it to the next parameter without affecting backward
// scan logic.
//
// In other words - let's push the closure argument through defaulted
// parameters until it can be considered extraneous if no parameters
// could possibly match it.
if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) &&
!parameterRequiresArgument(params, paramInfo, paramIdx)) {
if (llvm::none_of(
range(paramIdx + 1, params.size()), [&](unsigned idx) {
return parameterBindings[idx].empty() &&
(paramInfo.acceptsUnlabeledTrailingClosureArgument(
idx) ||
backwardScanAcceptsTrailingClosure(params[idx]));
})) {
haveUnfulfilledParams = true;
return;
}
// If one or more parameters can match the closure, let's check
// whether backward scan is applicable here.
}
// If this parameter does not require an argument, consider applying a
// backward-match rule that skips this parameter if doing so is the only
// way to successfully match arguments to parameters.
@@ -1076,8 +1105,10 @@ constraints::getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) {
class ArgumentFailureTracker : public MatchCallArgumentListener {
protected:
ConstraintSystem &CS;
NullablePtr<ValueDecl> Callee;
SmallVectorImpl<AnyFunctionType::Param> &Arguments;
ArrayRef<AnyFunctionType::Param> Parameters;
Optional<unsigned> UnlabeledTrailingClosureArgIndex;
ConstraintLocatorBuilder Locator;
private:
@@ -1109,11 +1140,14 @@ protected:
}
public:
ArgumentFailureTracker(ConstraintSystem &cs,
ArgumentFailureTracker(ConstraintSystem &cs, ValueDecl *callee,
SmallVectorImpl<AnyFunctionType::Param> &args,
ArrayRef<AnyFunctionType::Param> params,
Optional<unsigned> unlabeledTrailingClosureArgIndex,
ConstraintLocatorBuilder locator)
: CS(cs), Arguments(args), Parameters(params), Locator(locator) {}
: CS(cs), Callee(callee), Arguments(args), Parameters(params),
UnlabeledTrailingClosureArgIndex(unlabeledTrailingClosureArgIndex),
Locator(locator) {}
~ArgumentFailureTracker() override {
if (!MissingArguments.empty()) {
@@ -1143,6 +1177,19 @@ public:
if (!CS.shouldAttemptFixes())
return true;
// If this is a trailing closure, let's check if the call is
// to an init of a callable type. If so, let's not record it
// as extraneous since it would be matched against implicitly
// injected `.callAsFunction` call.
if (UnlabeledTrailingClosureArgIndex &&
argIdx == *UnlabeledTrailingClosureArgIndex && Callee) {
if (auto *ctor = dyn_cast<ConstructorDecl>(Callee.get())) {
auto resultTy = ctor->getResultInterfaceType();
if (resultTy->isCallableNominalType(CS.DC))
return true;
}
}
ExtraArguments.push_back(std::make_pair(argIdx, Arguments[argIdx]));
return false;
}
@@ -1251,12 +1298,15 @@ class CompletionArgumentTracker : public ArgumentFailureTracker {
struct CompletionArgInfo ArgInfo;
public:
CompletionArgumentTracker(ConstraintSystem &cs,
CompletionArgumentTracker(ConstraintSystem &cs, ValueDecl *callee,
SmallVectorImpl<AnyFunctionType::Param> &args,
ArrayRef<AnyFunctionType::Param> params,
Optional<unsigned> unlabeledTrailingClosureArgIndex,
ConstraintLocatorBuilder locator,
struct CompletionArgInfo ArgInfo)
: ArgumentFailureTracker(cs, args, params, locator), ArgInfo(ArgInfo) {}
: ArgumentFailureTracker(cs, callee, args, params,
unlabeledTrailingClosureArgIndex, locator),
ArgInfo(ArgInfo) {}
Optional<unsigned> missingArgument(unsigned paramIdx,
unsigned argInsertIdx) override {
@@ -1666,14 +1716,16 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
if (cs.isForCodeCompletion()) {
if (auto completionInfo = getCompletionArgInfo(locator.getAnchor(), cs)) {
listener = std::make_unique<CompletionArgumentTracker>(
cs, argsWithLabels, params, locator, *completionInfo);
cs, callee, argsWithLabels, params,
argList->getFirstTrailingClosureIndex(), locator, *completionInfo);
}
}
if (!listener) {
// We didn't create an argument tracker for code completion. Create a
// normal one.
listener = std::make_unique<ArgumentFailureTracker>(cs, argsWithLabels,
params, locator);
listener = std::make_unique<ArgumentFailureTracker>(
cs, callee, argsWithLabels, params,
argList->getFirstTrailingClosureIndex(), locator);
}
auto callArgumentMatch = constraints::matchCallArguments(
argsWithLabels, params, paramInfo,