mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[CSSimplify] Attempt to split trailing closures into an implicit .callAsFunction
In situations like `T(...) { ... }` where `T` is a callable type,
it's not immediately clear whether trailing closures are associated
with `.init` of `T` or implicit `.callAsFunction`. So if constructor
didn't match trailing closures, let's attempt to split them off and
form an implicit `.callAsFunction` call with them as a fallback.
This commit is contained in:
committed by
Pavel Yaskevich
parent
4acd8caf12
commit
c20621643b
@@ -10647,8 +10647,108 @@ ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
subKind, argumentsLoc, trailingClosureMatching);
|
||||
|
||||
switch (matchCallResult) {
|
||||
case SolutionKind::Error:
|
||||
case SolutionKind::Error: {
|
||||
if (shouldAttemptFixes())
|
||||
return SolutionKind::Error;
|
||||
|
||||
auto resultTy = func2->getResult();
|
||||
|
||||
// If this is a call that constructs a callable type with
|
||||
// a trailing closure(s), closure(s) might not belong to
|
||||
// the constructor but rather to implicit `callAsFunction`,
|
||||
// there is no way to determine that without trying.
|
||||
if (resultTy->isCallableNominalType(DC) &&
|
||||
argumentList->hasAnyTrailingClosures()) {
|
||||
auto *calleeLoc = getCalleeLocator(argumentsLoc);
|
||||
|
||||
bool isInit = false;
|
||||
if (auto overload = findSelectedOverloadFor(calleeLoc)) {
|
||||
isInit = bool(dyn_cast_or_null<ConstructorDecl>(
|
||||
overload->choice.getDeclOrNull()));
|
||||
}
|
||||
|
||||
if (!isInit)
|
||||
return SolutionKind::Error;
|
||||
|
||||
auto &ctx = getASTContext();
|
||||
auto numTrailing = argumentList->getNumTrailingClosures();
|
||||
|
||||
SmallVector<Argument, 4> newArguments;
|
||||
SmallVector<Argument, 4> trailingClosures;
|
||||
|
||||
for (unsigned i = 0, n = argumentList->size(); i != n; ++i) {
|
||||
if (argumentList->isTrailingClosureIndex(i)) {
|
||||
trailingClosures.push_back(argumentList->get(i));
|
||||
} else {
|
||||
newArguments.push_back(argumentList->get(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Original argument list with all the trailing closures removed.
|
||||
auto *newArgumentList = ArgumentList::createParsed(
|
||||
ctx, argumentList->getLParenLoc(), newArguments,
|
||||
argumentList->getRParenLoc(),
|
||||
/*firstTrailingClosureIndex=*/None);
|
||||
|
||||
auto trailingClosureTypes = func1->getParams().take_back(numTrailing);
|
||||
// The original result type is going to become a result of
|
||||
// implicit `.callAsFunction` instead since `.callAsFunction`
|
||||
// is inserted between `.init` and trailing closures.
|
||||
auto callAsFunctionResultTy = func1->getResult();
|
||||
|
||||
// The implicit replacement for original result type which
|
||||
// represents a callable type produced by `.init` call.
|
||||
auto callableType =
|
||||
createTypeVariable(getConstraintLocator({}), /*flags=*/0);
|
||||
|
||||
// The original application type with all the trailing closures
|
||||
// dropped from it and result replaced to the implicit variable.
|
||||
func1 = FunctionType::get(func1->getParams().drop_back(numTrailing),
|
||||
callableType, func1->getExtInfo());
|
||||
|
||||
auto matchCallResult = ::matchCallArguments(
|
||||
*this, func2, newArgumentList, func1->getParams(),
|
||||
func2->getParams(), subKind, argumentsLoc, trailingClosureMatching);
|
||||
|
||||
if (matchCallResult != SolutionKind::Solved)
|
||||
return SolutionKind::Error;
|
||||
|
||||
// Note that `createImplicit` cannot be used here because it
|
||||
// doesn't allow us to specify trailing closure index.
|
||||
auto *implicitCallArgumentList = ArgumentList::createParsed(
|
||||
ctx, /*LParen=*/SourceLoc(), trailingClosures,
|
||||
/*RParen=*/SourceLoc(),
|
||||
/*firstTrailingClosureIndex=*/0);
|
||||
|
||||
SmallVector<Identifier, 2> closureLabelsScratch;
|
||||
// Create implicit `.callAsFunction` expression to use as an anchor
|
||||
// for new argument list that only has trailing closures in it.
|
||||
auto *implicitCall = UnresolvedDotExpr::createImplicit(
|
||||
ctx, getAsExpr(argumentsLoc->getAnchor()), {ctx.Id_callAsFunction},
|
||||
implicitCallArgumentList->getArgumentLabels(closureLabelsScratch));
|
||||
|
||||
associateArgumentList(
|
||||
getConstraintLocator(implicitCall,
|
||||
ConstraintLocator::ApplyArgument),
|
||||
implicitCallArgumentList);
|
||||
|
||||
auto callAsFunctionArguments =
|
||||
FunctionType::get(trailingClosureTypes, callAsFunctionResultTy,
|
||||
FunctionType::ExtInfo());
|
||||
|
||||
// Form an unsolved constraint to apply trailing closures to a
|
||||
// callable type produced by `.init`. This constraint would become
|
||||
// active when `callableType` is bound.
|
||||
addUnsolvedConstraint(Constraint::create(
|
||||
*this, ConstraintKind::ApplicableFunction, callAsFunctionArguments,
|
||||
callableType,
|
||||
getConstraintLocator(implicitCall,
|
||||
ConstraintLocator::ApplyFunction)));
|
||||
break;
|
||||
}
|
||||
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
|
||||
case SolutionKind::Unsolved: {
|
||||
// Only occurs when there is an ambiguity between forward scanning and
|
||||
|
||||
Reference in New Issue
Block a user