mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge pull request #41189 from xedin/trailing-closures-with-callAsFunction
[ConstraintSystem] Match trailing closures to implicit `.callAsFunction` when necessary
This commit is contained in:
@@ -1321,6 +1321,7 @@ public:
|
||||
// Match the argument of a call to the parameter.
|
||||
ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
|
||||
ConstraintSystem &cs, FunctionType *contextualType,
|
||||
ArgumentList *argList,
|
||||
ArrayRef<AnyFunctionType::Param> args,
|
||||
ArrayRef<AnyFunctionType::Param> params, ConstraintKind subKind,
|
||||
ConstraintLocatorBuilder locator,
|
||||
@@ -1340,8 +1341,7 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
|
||||
|
||||
ParameterListInfo paramInfo(params, callee, appliedSelf);
|
||||
|
||||
// Dig out the argument information.
|
||||
auto *argList = cs.getArgumentList(loc);
|
||||
// Make sure that argument list is available.
|
||||
assert(argList);
|
||||
|
||||
// Apply labels to arguments.
|
||||
@@ -10494,6 +10494,32 @@ bool ConstraintSystem::simplifyAppliedOverloads(
|
||||
numOptionalUnwraps, locator);
|
||||
}
|
||||
|
||||
/// Create an implicit dot-member reference expression to be used
|
||||
/// as a root for injected `.callAsFunction` call.
|
||||
static UnresolvedDotExpr *
|
||||
createImplicitRootForCallAsFunction(ConstraintSystem &cs, Type refType,
|
||||
ArgumentList *arguments,
|
||||
ConstraintLocator *calleeLocator) {
|
||||
auto &ctx = cs.getASTContext();
|
||||
auto *baseExpr = castToExpr(calleeLocator->getAnchor());
|
||||
|
||||
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 *implicitRef = UnresolvedDotExpr::createImplicit(
|
||||
ctx, baseExpr, {ctx.Id_callAsFunction},
|
||||
arguments->getArgumentLabels(closureLabelsScratch));
|
||||
|
||||
{
|
||||
// Record a type of the new reference in the constraint system.
|
||||
cs.setType(implicitRef, refType);
|
||||
// Record new `.callAsFunction` in the constraint system.
|
||||
cs.recordCallAsFunction(implicitRef, arguments, calleeLocator);
|
||||
}
|
||||
|
||||
return implicitRef;
|
||||
}
|
||||
|
||||
ConstraintSystem::SolutionKind
|
||||
ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
Type type1, Type type2,
|
||||
@@ -10548,17 +10574,20 @@ ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
};
|
||||
|
||||
// Local function to form an unsolved result.
|
||||
auto formUnsolved = [&] {
|
||||
auto formUnsolved = [&](bool activate = false) {
|
||||
if (flags.contains(TMF_GenerateConstraints)) {
|
||||
addUnsolvedConstraint(
|
||||
Constraint::createApplicableFunction(
|
||||
auto *application = Constraint::createApplicableFunction(
|
||||
*this, type1, type2, trailingClosureMatching,
|
||||
getConstraintLocator(locator)));
|
||||
getConstraintLocator(locator));
|
||||
|
||||
addUnsolvedConstraint(application);
|
||||
if (activate)
|
||||
activateConstraint(application);
|
||||
|
||||
return SolutionKind::Solved;
|
||||
}
|
||||
|
||||
return SolutionKind::Unsolved;
|
||||
|
||||
};
|
||||
|
||||
// If right-hand side is a type variable, the constraint is unsolved.
|
||||
@@ -10633,15 +10662,97 @@ ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
? ConstraintKind::OperatorArgumentConversion
|
||||
: ConstraintKind::ArgumentConversion);
|
||||
|
||||
auto *argumentsLoc = getConstraintLocator(
|
||||
outerLocator.withPathElement(ConstraintLocator::ApplyArgument));
|
||||
|
||||
auto *argumentList = getArgumentList(argumentsLoc);
|
||||
// The argument type must be convertible to the input type.
|
||||
auto matchCallResult = ::matchCallArguments(
|
||||
*this, func2, func1->getParams(), func2->getParams(), subKind,
|
||||
outerLocator.withPathElement(ConstraintLocator::ApplyArgument),
|
||||
trailingClosureMatching);
|
||||
*this, func2, argumentList, func1->getParams(), func2->getParams(),
|
||||
subKind, argumentsLoc, trailingClosureMatching);
|
||||
|
||||
switch (matchCallResult) {
|
||||
case SolutionKind::Error:
|
||||
case SolutionKind::Error: {
|
||||
auto resultTy = func2->getResult();
|
||||
|
||||
// If this is a call that constructs a callable type with
|
||||
// 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(
|
||||
argumentList->getNonTrailingArgs());
|
||||
SmallVector<Argument, 4> trailingClosures(
|
||||
argumentList->getTrailingClosures());
|
||||
|
||||
// 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;
|
||||
|
||||
auto *implicitCallArgumentList =
|
||||
ArgumentList::createImplicit(ctx, trailingClosures,
|
||||
/*firstTrailingClosureIndex=*/0);
|
||||
|
||||
auto *implicitRef = createImplicitRootForCallAsFunction(
|
||||
*this, callAsFunctionResultTy, implicitCallArgumentList, calleeLoc);
|
||||
|
||||
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(implicitRef,
|
||||
ConstraintLocator::ApplyFunction)));
|
||||
break;
|
||||
}
|
||||
|
||||
return SolutionKind::Error;
|
||||
}
|
||||
|
||||
case SolutionKind::Unsolved: {
|
||||
// Only occurs when there is an ambiguity between forward scanning and
|
||||
@@ -10691,6 +10802,26 @@ ConstraintSystem::simplifyApplicableFnConstraint(
|
||||
if (instance2->isTypeVariableOrMember())
|
||||
return formUnsolved();
|
||||
|
||||
auto *argumentsLoc = getConstraintLocator(
|
||||
outerLocator.withPathElement(ConstraintLocator::ApplyArgument));
|
||||
|
||||
auto *argumentList = getArgumentList(argumentsLoc);
|
||||
assert(argumentList);
|
||||
|
||||
// Cannot simplify construction of callable types during constraint
|
||||
// generation when trailing closures are present because such calls
|
||||
// have special trailing closure matching semantics. It's unclear
|
||||
// whether trailing arguments belong to `.init` or implicit
|
||||
// `.callAsFunction` in this case.
|
||||
//
|
||||
// Note that the constraint has to be activate so that solver attempts
|
||||
// once constraint generation is done.
|
||||
if (getPhase() == ConstraintSystemPhase::ConstraintGeneration &&
|
||||
argumentList->hasAnyTrailingClosures() &&
|
||||
instance2->isCallableNominalType(DC)) {
|
||||
return formUnsolved(/*activate=*/true);
|
||||
}
|
||||
|
||||
// Construct the instance from the input arguments.
|
||||
auto simplified = simplifyConstructionConstraint(instance2, func1, subflags,
|
||||
/*FIXME?*/ DC,
|
||||
@@ -11579,6 +11710,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl(
|
||||
if (!ArgumentLists.count(memberLoc)) {
|
||||
auto *argList = ArgumentList::createImplicit(
|
||||
getASTContext(), {Argument(SourceLoc(), Identifier(), nullptr)},
|
||||
/*firstTrailingClosureIndex=*/None,
|
||||
AllocationArena::ConstraintSolver);
|
||||
ArgumentLists.insert({memberLoc, argList});
|
||||
}
|
||||
@@ -11867,6 +11999,15 @@ void ConstraintSystem::recordMatchCallArgumentResult(
|
||||
argumentMatchingChoices.insert({locator, result});
|
||||
}
|
||||
|
||||
void ConstraintSystem::recordCallAsFunction(UnresolvedDotExpr *root,
|
||||
ArgumentList *arguments,
|
||||
ConstraintLocator *locator) {
|
||||
ImplicitCallAsFunctionRoots.insert({locator, root});
|
||||
|
||||
associateArgumentList(
|
||||
getConstraintLocator(root, ConstraintLocator::ApplyArgument), arguments);
|
||||
}
|
||||
|
||||
ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
||||
ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind,
|
||||
TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
|
||||
|
||||
Reference in New Issue
Block a user